[Darshan-commits] [Darshan] branch, dev-modular, updated. darshan-2.3.1-100-g5ecf401

Service Account git at mcs.anl.gov
Wed Apr 8 16:00:52 CDT 2015


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "".

The branch, dev-modular has been updated
       via  5ecf4013b55c356441590ce97e4ef29cf03d3a1f (commit)
      from  233f1607498e8197f127a5b2712f1593e752ae28 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 5ecf4013b55c356441590ce97e4ef29cf03d3a1f
Author: Shane Snyder <ssnyder at mcs.anl.gov>
Date:   Wed Apr 8 15:59:58 2015 -0500

    more docs + "NULL" example module implementation

-----------------------------------------------------------------------

Summary of changes:
 darshan-log-format.h                |    9 +-
 darshan-mpiio-log-format.h          |    5 +-
 darshan-null-log-format.h           |   47 ++++
 darshan-runtime/Makefile.in         |   18 +-
 darshan-runtime/darshan.h           |   46 ++--
 darshan-runtime/lib/darshan-core.c  |    9 +-
 darshan-runtime/lib/darshan-mpiio.c |   12 +-
 darshan-runtime/lib/darshan-null.c  |  382 +++++++++++++++++++++++++++
 darshan-runtime/lib/darshan-posix.c |   20 +-
 darshan-runtime/utlist.h            |  490 +++++++++++++++++++++++++++++++++++
 doc/darshan-modularization.txt      |    7 +-
 11 files changed, 994 insertions(+), 51 deletions(-)
 create mode 100644 darshan-null-log-format.h
 create mode 100644 darshan-runtime/lib/darshan-null.c
 create mode 100644 darshan-runtime/utlist.h


Diff of changes:
diff --git a/darshan-log-format.h b/darshan-log-format.h
index 7c49cbb..8886700 100644
--- a/darshan-log-format.h
+++ b/darshan-log-format.h
@@ -1,6 +1,7 @@
 /*
- *  (C) 2009 by Argonne National Laboratory.
- *      See COPYRIGHT in top-level directory.
+ * Copyright (C) 2015 University of Chicago.
+ * See COPYRIGHT notice in top-level directory.
+ *
  */
 
 #ifndef __DARSHAN_LOG_FORMAT_H
@@ -39,7 +40,8 @@ typedef uint64_t darshan_record_id;
 #define DARSHAN_MAX_MODS 16
 typedef enum
 {
-    DARSHAN_POSIX_MOD = 0,
+    DARSHAN_NULL_MOD = 0,
+    DARSHAN_POSIX_MOD,
     DARSHAN_MPIIO_MOD,
     DARSHAN_HDF5_MOD,
     DARSHAN_PNETCDF_MOD,
@@ -47,6 +49,7 @@ typedef enum
 
 static char * const darshan_module_names[] =
 {
+    "NULL",
     "POSIX",
     "MPI-IO",
     "HDF5",
diff --git a/darshan-mpiio-log-format.h b/darshan-mpiio-log-format.h
index 48a10f0..857133d 100644
--- a/darshan-mpiio-log-format.h
+++ b/darshan-mpiio-log-format.h
@@ -1,6 +1,7 @@
 /*
- *  (C) 2015 by Argonne National Laboratory.
- *      See COPYRIGHT in top-level directory.
+ * Copyright (C) 2015 University of Chicago.
+ * See COPYRIGHT notice in top-level directory.
+ *
  */
 
 #ifndef __DARSHAN_MPIIO_LOG_FORMAT_H
diff --git a/darshan-null-log-format.h b/darshan-null-log-format.h
new file mode 100644
index 0000000..152dd1a
--- /dev/null
+++ b/darshan-null-log-format.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 University of Chicago.
+ * See COPYRIGHT notice in top-level directory.
+ *
+ */
+
+#ifndef __DARSHAN_NULL_LOG_FORMAT_H
+#define __DARSHAN_NULL_LOG_FORMAT_H
+
+#include "darshan-log-format.h"
+
+/* integer counters for the "NULL" example module */
+enum darshan_null_indices
+{
+    NULL_BARS,              /* count of number of 'bar' function calls */
+    NULL_BAR_DAT,           /* arbitrary data value set by last call to 'bar' */
+
+    NULL_NUM_INDICES,
+};
+
+/* floating point counters for the "NULL" example module */
+enum darshan_null_f_indices
+{
+    NULL_F_BAR_TIMESTAMP,   /* timestamp of the first call to function 'bar' */
+    NULL_F_BAR_DURATION,    /* timer indicating duration of last call to 'bar' */
+
+    NULL_F_NUM_INDICES,
+};
+
+/* the darshan_null_record structure encompasses the high-level data/counters
+ * which would actually be logged to file by Darshan for the "NULL" example
+ * module. This example implementation logs the following data for each
+ * record:
+ *      - a corresponding Darshan record identifier
+ *      - the rank of the process responsible for the record
+ *      - integer I/O counters (operation counts, I/O sizes, etc.)
+ *      - floating point I/O counters (timestamps, cumulative timers, etc.)
+ */
+struct darshan_null_record
+{
+    darshan_record_id f_id;
+    int64_t rank;
+    int64_t counters[NULL_NUM_INDICES];
+    double fcounters[NULL_F_NUM_INDICES];
+};
+
+#endif /* __DARSHAN_NULL_LOG_FORMAT_H */
diff --git a/darshan-runtime/Makefile.in b/darshan-runtime/Makefile.in
index 791cac6..be3485b 100644
--- a/darshan-runtime/Makefile.in
+++ b/darshan-runtime/Makefile.in
@@ -1,4 +1,4 @@
-all: lib/libdarshan.a
+all: lib/libdarshan.a lib/darshan-null.o
 
 DESTDIR =
 srcdir = @srcdir@
@@ -19,7 +19,7 @@ DARSHAN_LOG_FORMAT = $(srcdir)/../darshan-log-format.h
 DARSHAN_VERSION = @DARSHAN_VERSION@
 
 ifndef DISABLE_LDPRELOAD
-all: lib/libdarshan.so 
+all: lib/libdarshan.so lib/darshan-null.po
 endif
 
 VPATH = $(srcdir)
@@ -51,16 +51,22 @@ lib/darshan-common.o: lib/darshan-common.c darshan.h $(DARSHAN_LOG_FORMAT) | lib
 lib/darshan-common.po: lib/darshan-common.c darshan.h $(DARSHAN_LOG_FORMAT) | lib
 	$(CC) $(CFLAGS_SHARED) -c $< -o $@
 
-lib/darshan-posix.o: lib/darshan-posix.c darshan.h $(DARSHAN_LOG_FORMAT) | lib
+lib/darshan-null.o: lib/darshan-null.c darshan.h $(DARSHAN_LOG_FORMAT) $(srcdir)/../darshan-null-log-format.h | lib
 	$(CC) $(CFLAGS) -c $< -o $@
 
-lib/darshan-posix.po: lib/darshan-posix.c darshan.h $(DARSHAN_LOG_FORMAT) | lib
+lib/darshan-null.po: lib/darshan-null.c darshan.h $(DARSHAN_LOG_FORMAT) $(srcdir)/../darshan-null-log-format.h | lib
 	$(CC) $(CFLAGS_SHARED) -c $< -o $@
 
-lib/darshan-mpiio.o: lib/darshan-mpiio.c darshan.h $(DARSHAN_LOG_FORMAT) | lib
+lib/darshan-posix.o: lib/darshan-posix.c darshan.h $(DARSHAN_LOG_FORMAT) $(srcdir)/../darshan-posix-log-format.h | lib
 	$(CC) $(CFLAGS) -c $< -o $@
 
-lib/darshan-mpiio.po: lib/darshan-mpiio.c darshan.h $(DARSHAN_LOG_FORMAT) | lib
+lib/darshan-posix.po: lib/darshan-posix.c darshan.h $(DARSHAN_LOG_FORMAT) $(srcdir)/../darshan-posix-log-format.h | lib
+	$(CC) $(CFLAGS_SHARED) -c $< -o $@
+
+lib/darshan-mpiio.o: lib/darshan-mpiio.c darshan.h $(DARSHAN_LOG_FORMAT) $(srcdir)/../darshan-mpiio-log-format.h | lib
+	$(CC) $(CFLAGS) -c $< -o $@
+
+lib/darshan-mpiio.po: lib/darshan-mpiio.c darshan.h $(DARSHAN_LOG_FORMAT) $(srcdir)/../darshan-mpiio-log-format.h | lib
 	$(CC) $(CFLAGS_SHARED) -c $< -o $@
 
 lib/lookup3.o: lib/lookup3.c
diff --git a/darshan-runtime/darshan.h b/darshan-runtime/darshan.h
index b9c7066..6016d24 100644
--- a/darshan-runtime/darshan.h
+++ b/darshan-runtime/darshan.h
@@ -34,38 +34,43 @@
 #include <dlfcn.h>
 #include <stdlib.h>
 
-#define DARSHAN_FORWARD_DECL(name,ret,args) \
-  ret (*__real_ ## name)args = NULL
+#define DARSHAN_FORWARD_DECL(__func,__ret,__args) \
+  __ret (*__real_ ## __func)__args = NULL
 
-#define DARSHAN_DECL(__name) __name
+#define DARSHAN_DECL(__func) __func
 
-#define DARSHAN_MPI_CALL(func) __real_ ## func
+#define DARSHAN_MPI_CALL(__func) __real_ ## __func
 
-#define MAP_OR_FAIL(func) \
-    if (!(__real_ ## func)) \
+#define MAP_OR_FAIL(__func) \
+    if (!(__real_ ## __func)) \
     { \
-        __real_ ## func = dlsym(RTLD_NEXT, #func); \
-        if(!(__real_ ## func)) { \
-           fprintf(stderr, "Darshan failed to map symbol: %s\n", #func); \
+        __real_ ## __func = dlsym(RTLD_NEXT, #__func); \
+        if(!(__real_ ## __func)) { \
+           fprintf(stderr, "Darshan failed to map symbol: %s\n", #__func); \
            exit(1); \
        } \
     }
 
 #else
 
-#define DARSHAN_FORWARD_DECL(name,ret,args) \
-  extern ret __real_ ## name args;
+#define DARSHAN_FORWARD_DECL(__name,__ret,__args) \
+  extern __ret __real_ ## __name __args;
 
 #define DARSHAN_DECL(__name) __wrap_ ## __name
 
-#define DARSHAN_MPI_CALL(func) func
+#define DARSHAN_MPI_CALL(__func) __func
 
-#define MAP_OR_FAIL(func)
+#define MAP_OR_FAIL(__func)
 
 #endif
 
-/* macros for manipulating module's counter variables */
-/* NOTE: */
+/* macros for manipulating a module's counter variables */
+/* NOTE: These macros assume a module's record stores integer
+ * and floating point counters in arrays, named counters and
+ * fcounters, respectively. __rec_p is the a pointer to the
+ * data record, __counter is the counter in question, and
+ * __value is the corresponding data value.
+ */
 #define DARSHAN_COUNTER_SET(__rec_p, __counter, __value) do{ \
     (__rec_p)->counters[__counter] = __value; \
 } while(0)
@@ -102,6 +107,11 @@
         (__rec_p)->counters[__counter] = __value; \
 } while(0)
 
+/* NOTE: This macro is for storing histogram counters with 10
+ * distinct buckets. For an example of how it is used, consult
+ * the POSIX module implementation, which stores histograms of
+ * access sizes for POSIX I/O functions.
+ */
 #define DARSHAN_BUCKET_INC(__rec_p, __counter_base, __value) do {\
     if(__value < 101) \
         (__rec_p)->counters[__counter_base] += 1; \
@@ -128,10 +138,10 @@
 /* module developers provide the following functions to darshan-core */
 struct darshan_module_funcs
 {
-    /* disable futher instrumentation within a module */
-    void (*disable_instrumentation)(void);
+    /* perform any necessary pre-shutdown steps */
+    void (*begin_shutdown)(void);
     /* perform any necessary steps prior to reducing */
-    void (*prepare_for_reduction)(
+    void (*setup_reduction)(
         darshan_record_id *shared_recs, /* input list of shared records */
         int *shared_rec_count, /* in/out shared record count */
         void **send_buf, /* send buffer for shared file reduction */
diff --git a/darshan-runtime/lib/darshan-core.c b/darshan-runtime/lib/darshan-core.c
index 55006bc..aee8c6f 100644
--- a/darshan-runtime/lib/darshan-core.c
+++ b/darshan-runtime/lib/darshan-core.c
@@ -258,14 +258,15 @@ void darshan_core_shutdown()
     darshan_core = NULL;
 
     /* we also need to set which modules were registered on this process and
-     * disable tracing within those modules while we shutdown
+     * call into those modules and give them a chance to perform any necessary
+     * pre-shutdown steps.
      */
     for(i = 0; i < DARSHAN_MAX_MODS; i++)
     {
         if(final_core->mod_array[i])
         {
             local_mod_use[i] = 1;
-            final_core->mod_array[i]->mod_funcs.disable_instrumentation();
+            final_core->mod_array[i]->mod_funcs.begin_shutdown();
         }
     }
     DARSHAN_CORE_UNLOCK();
@@ -492,10 +493,10 @@ void darshan_core_shutdown()
             }
 
             /* if there are globally shared files, do a shared file reduction */
-            if(shared_rec_count && this_mod->mod_funcs.prepare_for_reduction &&
+            if(shared_rec_count && this_mod->mod_funcs.setup_reduction &&
                this_mod->mod_funcs.record_reduction_op)
             {
-                this_mod->mod_funcs.prepare_for_reduction(mod_shared_recs, &shared_rec_count,
+                this_mod->mod_funcs.setup_reduction(mod_shared_recs, &shared_rec_count,
                     &red_send_buf, &red_recv_buf, &rec_sz);
 
                 if(shared_rec_count)
diff --git a/darshan-runtime/lib/darshan-mpiio.c b/darshan-runtime/lib/darshan-mpiio.c
index 67f30a5..2d3a82d 100644
--- a/darshan-runtime/lib/darshan-mpiio.c
+++ b/darshan-runtime/lib/darshan-mpiio.c
@@ -114,7 +114,7 @@ static int my_rank = -1;
 #define MPIIO_UNLOCK() pthread_mutex_unlock(&mpiio_runtime_mutex)
 
 static void mpiio_runtime_initialize(void);
-static void mpiio_disable_instrumentation(void);
+static void mpiio_begin_shutdown(void);
 static void mpiio_shutdown(void);
 static void mpiio_get_output_data(
     void **buffer,
@@ -123,7 +123,7 @@ static struct mpiio_file_runtime* mpiio_file_by_name_setfh(const char* name, MPI
 static struct mpiio_file_runtime* mpiio_file_by_name(const char *name);
 static void mpiio_record_reduction_op(void* infile_v, void* inoutfile_v,
     int *len, MPI_Datatype *datatype);
-static void mpiio_prepare_for_reduction(darshan_record_id *shared_recs,
+static void mpiio_setup_reduction(darshan_record_id *shared_recs,
     int *shared_rec_count, void **send_buf, void **recv_buf, int *rec_size);
 static int mpiio_file_compare(const void* a, const void* b);
 
@@ -192,8 +192,8 @@ static void mpiio_runtime_initialize()
     int mem_limit;
     struct darshan_module_funcs mpiio_mod_fns =
     {
-        .disable_instrumentation = &mpiio_disable_instrumentation,
-        .prepare_for_reduction = &mpiio_prepare_for_reduction,
+        .begin_shutdown = &mpiio_begin_shutdown,
+        .setup_reduction = &mpiio_setup_reduction,
         .record_reduction_op = &mpiio_record_reduction_op,
         .get_output_data = &mpiio_get_output_data,
         .shutdown = &mpiio_shutdown
@@ -245,7 +245,7 @@ static void mpiio_runtime_initialize()
     return;
 }
 
-static void mpiio_disable_instrumentation()
+static void mpiio_begin_shutdown()
 {
     assert(mpiio_runtime);
 
@@ -444,7 +444,7 @@ static void mpiio_record_reduction_op(
     return;
 }
 
-static void mpiio_prepare_for_reduction(
+static void mpiio_setup_reduction(
     darshan_record_id *shared_recs,
     int *shared_rec_count,
     void **send_buf,
diff --git a/darshan-runtime/lib/darshan-null.c b/darshan-runtime/lib/darshan-null.c
new file mode 100644
index 0000000..0a864e2
--- /dev/null
+++ b/darshan-runtime/lib/darshan-null.c
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2015 University of Chicago.
+ * See COPYRIGHT notice in top-level directory.
+ *
+ */
+
+#define _XOPEN_SOURCE 500
+#define _GNU_SOURCE
+
+#include "darshan-runtime-config.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <assert.h>
+
+#include "uthash.h"
+#include "darshan.h"
+#include "darshan-null-log-format.h"
+
+/* The "NULL" module is an example instrumentation module implementation provided
+ * with Darshan, primarily to indicate how arbitrary modules may be integrated
+ * into Darshan. In particular, this module demonstrates how to develop wrapper
+ * functions for intercepting functions of interest, how to best manage necessary
+ * runtime data structures, and how to coordinate with the darshan-core component,
+ * among other things. This module is not linked with the darshan-runtime library; 
+ * it is intended mainly to serve as a basic stubbed out module implementation
+ * that may be reused and expanded on by developers adding new instrumentation modules.
+ */
+
+/* TODO: this probably shouldn't be here -- LD_PRELOADing POSIX wrappers will cause MPI linker dependency */
+#ifdef DARSHAN_PRELOAD
+extern double (*__real_PMPI_Comm_rank)(MPI_Comm comm, int *rank);
+#endif
+
+/* The DARSHAN_FORWARD_DECL macro (defined in darshan.h) is used to provide forward
+ * declarations for wrapped funcions, regardless if Darshan is used with statically
+ * or dynamically linked executables.
+ */
+DARSHAN_FORWARD_DECL(foo, int, (const char *name, int arg1, int arg2));
+
+
+/* The null_record_runtime structure maintains necessary runtime metadata
+ * for a "NULL" module data record (darshan_null_record structure, defined
+ * in darshan-null-log-format.h). This metadata assists with the instrumenting
+ * of specific statistics in the file record.
+ *
+ * RATIONALE: In general, a module may need to track some stateful, volatile 
+ * information regarding specific I/O statistics to aid in the instrumentation
+ * process. However, this information should not be stored in the darshan_null_record
+ * struct because we don't want it to appear in the final darshan log file.
+ * We therefore associate a null_record_runtime structure with each darshan_null_record
+ * structure in order to track this information.
+ *
+ * NOTE: The null_record_runtime struct contains a pointer to a darshan_null_record
+ * struct (see the *record_p member) rather than simply embedding an entire
+ * darshan_null_record struct.  This is done so that all of the darshan_null_record
+ * structs can be kept contiguous in memory as a single array to simplify
+ * reduction, compression, and storage.
+ */
+struct null_record_runtime
+{
+    /* Darshan record for the "NULL" example module */
+    struct darshan_null_record* record_p;
+
+    /* ... other runtime data ... */
+
+    /* hash table link for this record */
+    /* NOTE: it is entirely up to the module developer how to persist module
+     * records in memory as the instrumented application runs. These records
+     * could just as easily be stored in an array or linked list. That said,
+     * the data structure selection should be mindful of the resulting memory
+     * footprint and search time complexity to attempt minimize Darshan overheads.
+     * hash table and linked list implementations are available in uthash.h and
+     * utlist.h, respectively.
+     */
+    UT_hash_handle hlink;
+};
+
+/* The null_runtime structure simply encapsulates global data structures needed
+ * by the module for instrumenting functions of interest and providing the output
+ * I/O data for this module to the darshan-core component at shutdown time.
+ */
+struct null_runtime
+{
+    /* runtime_record_array is the array of runtime records for the "NULL" module. */
+    struct null_record_runtime* runtime_record_array;
+    /* record_array is the array of high-level Darshan records for the "NULL" module,
+     * each corresponding to the the runtime record structure stored at the same array
+     * index in runtime_record_array.
+     */
+    struct darshan_null_record* record_array;
+    /* file_array_size is the maximum amount of records that can be stored in 
+     * record_array (and consequentially, runtime_record_array).
+     */
+    int rec_array_size;
+    /* file_array_ndx is the current index into both runtime_record_array and
+     * record_array.
+     */
+    int rec_array_ndx;
+    /* record_hash is a pointer to a hash table of null_record_runtime structures
+     * currently maintained by the "NULL" module.
+     */
+    struct null_record_runtime* record_hash;
+};
+
+/* null_runtime is the global data structure encapsulating "NULL" module state */
+static struct null_runtime *null_runtime = NULL;
+/* The null_runtime_mutex is a lock used when updating the null_runtime global
+ * structure (or any other global data structures). This is necessary to avoid race
+ * conditions as multiple threads execute function wrappers and update module state.
+ * NOTE: Recursive mutexes are used in case functions wrapped by this module call
+ * other wrapped functions that would result in deadlock, otherwise. This mechanism
+ * may not be necessary for all instrumentation modules.
+ */
+static pthread_mutex_t null_runtime_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+/* the instrumentation_disabled flag is used to toggle wrapper functions on/off */
+static int instrumentation_disabled = 0;
+/* my_rank indicates the MPI rank of this process */
+static int my_rank = -1;
+
+/* internal helper functions for the "NULL" module */
+static void null_runtime_initialize(void);
+static struct null_record_runtime* null_record_by_name(const char *name);
+
+/* forward declaration for module functions needed to interface with darshan-core */
+static void null_begin_shutdown(void);
+static void null_get_output_data(void **buffer, int *size);
+static void null_shutdown(void);
+
+/* macros for obtaining/releasing the "NULL" module lock */
+#define NULL_LOCK() pthread_mutex_lock(&null_runtime_mutex)
+#define NULL_UNLOCK() pthread_mutex_unlock(&null_runtime_mutex)
+
+/* macro for instrumenting the "NULL" module's foo function */
+/* NOTE: this macro makes use of the DARSHAN_COUNTER_* macros defined
+ * and documented in darshan.h.
+ */
+#define NULL_RECORD_FOO(__ret, __name, __dat, __tm1, __tm2) do{ \
+    struct null_record_runtime* rec; \
+    double elapsed = __tm2 - __tm1; \
+    /* if foo returns error (return code < 0), don't instrument anything */ \
+    if(__ret < 0) break; \
+    /* use '__name' to lookup a corresponding "NULL" record */ \
+    rec = null_record_by_name(__name); \
+    if(!rec) break; \
+    /* increment counter indicating number of calls to 'bar' */ \
+    DARSHAN_COUNTER_INC(rec->record_p, NULL_BARS, 1); \
+    /* store data value for most recent call to 'bar' */ \
+    DARSHAN_COUNTER_SET(rec->record_p, NULL_BAR_DAT, __dat); \
+    /* store timestamp of most recent call to 'bar' */ \
+    DARSHAN_COUNTER_F_SET(rec->record_p, NULL_F_BAR_TIMESTAMP, __tm1); \
+    /* store duration of most recent call to 'bar' */ \
+    DARSHAN_COUNTER_F_SET(rec->record_p, NULL_F_BAR_DURATION, elapsed); \
+} while(0)
+
+/**********************************************************
+ *    Wrappers for "NULL" module functions of interest    * 
+ **********************************************************/
+
+/* The DARSHAN_DECL macro provides the appropriate wrapper function names,
+ * depending on whether the Darshan library is statically or dynamically linked.
+ */
+int DARSHAN_DECL(foo)(const char* name, int arg1, int arg2)
+{
+    ssize_t ret;
+    double tm1, tm2;
+
+    /* The MAP_OR_FAIL macro attempts to obtain the address of the actual
+     * underlying foo function call (__real_foo), in the case of LD_PRELOADing
+     * the Darshan library. For statically linked executables, this macro is
+     * just a NOP. 
+     */
+    MAP_OR_FAIL(foo);
+
+    /* In general, Darshan wrappers begin by calling the real version of the
+     * given wrapper function. Timers are used to record the duration of this
+     * operation. */
+    tm1 = darshan_core_wtime();
+    ret = __real_foo(name, arg1, arg2);
+    tm2 = darshan_core_wtime();
+
+    NULL_LOCK();
+
+    /* Before attempting to instrument I/O statistics for function foo, make
+     * sure the "NULL" module runtime environment has been initialized. 
+     * NOTE: this runtime environment is initialized only once -- if the
+     * appropriate structures have already been initialized, this function simply
+     * returns.
+     */
+    null_runtime_initialize();
+
+    /* Call macro for instrumenting data for foo function calls. */
+    NULL_RECORD_FOO(ret, name, arg1+arg2, tm1, tm2);
+
+    NULL_UNLOCK();
+
+    return(ret);
+}
+
+/**********************************************************
+ * Internal functions for manipulating POSIX module state *
+ **********************************************************/
+
+/* Initialize internal POSIX module data structures and register with darshan-core. */
+static void null_runtime_initialize()
+{
+    /* struct of function pointers for interfacing with darshan-core */
+    struct darshan_module_funcs null_mod_fns =
+    {
+        .begin_shutdown = &null_begin_shutdown,
+        .setup_reduction = NULL,        /* "NULL" module does not do reductions */
+        .record_reduction_op = NULL,    /* "NULL" module does not do reductions */
+        .get_output_data = &null_get_output_data,
+        .shutdown = &null_shutdown
+    };
+    int mem_limit; /* max. memory this module can consume, dictated by darshan-core */
+
+    /* don't do anything if already initialized or instrumenation is disabled */
+    if(null_runtime || instrumentation_disabled)
+        return;
+
+    /* register the "NULL" module with the darshan-core component */
+    darshan_core_register_module(
+        DARSHAN_NULL_MOD,   /* Darshan module identifier, defined in darshan-log-format.h */
+        &null_mod_fns,
+        &mem_limit,
+        NULL);
+
+    /* return if no memory assigned by darshan-core */
+    if(mem_limit == 0)
+        return;
+
+    /* initialize module's global state */
+    null_runtime = malloc(sizeof(*null_runtime));
+    if(!null_runtime)
+        return;
+    memset(null_runtime, 0, sizeof(*null_runtime));
+
+    /* Set the maximum number of data records this module may track, as indicated
+     * by mem_limit (set by darshan-core).
+     * NOTE: We interpret the maximum memory limit to be related to the maximum
+     * amount of data which may be written to log by a single process for a given
+     * module. We therefore use this maximum memory limit to determine how many
+     * darshan_null_record structures we can track per process.
+     */
+    null_runtime->rec_array_size = mem_limit / sizeof(struct darshan_null_record);
+    null_runtime->rec_array_ndx = 0;
+
+    /* allocate both record arrays (runtime and high-level records) */
+    null_runtime->runtime_record_array = malloc(null_runtime->rec_array_size *
+                                                sizeof(struct null_record_runtime));
+    null_runtime->record_array = malloc(null_runtime->rec_array_size *
+                                        sizeof(struct darshan_null_record));
+    if(!null_runtime->runtime_record_array || !null_runtime->record_array)
+    {
+        null_runtime->rec_array_size = 0;
+        return;
+    }
+    memset(null_runtime->runtime_record_array, 0, null_runtime->rec_array_size *
+           sizeof(struct null_record_runtime));
+    memset(null_runtime->record_array, 0, null_runtime->rec_array_size *
+           sizeof(struct darshan_null_record));
+
+    /* TODO: we should move this out of here.. perhaps register_module returns rank? */
+    DARSHAN_MPI_CALL(PMPI_Comm_rank)(MPI_COMM_WORLD, &my_rank);
+
+    return;
+}
+
+/* Search for and return a "NULL" module record corresponding to name parameter. */
+static struct null_record_runtime* null_record_by_name(const char *name)
+{
+    struct null_record_runtime *rec = NULL;
+    darshan_record_id rec_id;
+
+    /* Don't search for a record if the "NULL" module is not initialized or
+     * if instrumentation has been toggled off.
+     */
+    if(!null_runtime || instrumentation_disabled)
+        return(NULL);
+
+    /* get a unique record identifier for this record from darshan-core */
+    darshan_core_register_record(
+        (void*)name,
+        strlen(name),
+        1,
+        DARSHAN_NULL_MOD,
+        &rec_id,
+        NULL);
+
+    /* search the hash table for this file record, and return if found */
+    HASH_FIND(hlink, null_runtime->record_hash, &rec_id, sizeof(darshan_record_id), rec);
+    if(rec)
+    {
+        return(rec);
+    }
+
+    if(null_runtime->rec_array_ndx < null_runtime->rec_array_size);
+    {
+        /* no existing record, assign a new one from the global array */
+        rec = &(null_runtime->runtime_record_array[null_runtime->rec_array_ndx]);
+        rec->record_p = &(null_runtime->record_array[null_runtime->rec_array_ndx]);
+
+        /* set the darshan record id and corresponding process rank for this record */
+        rec->record_p->f_id = rec_id;
+        rec->record_p->rank = my_rank;
+
+        /* add new record to file hash table */
+        HASH_ADD(hlink, null_runtime->record_hash, record_p->f_id, sizeof(darshan_record_id), rec);
+
+        null_runtime->rec_array_ndx++;
+    }
+
+    return(rec);
+}
+
+/******************************************************************************
+ * Functions exported by the "NULL" module for coordinating with darshan-core *
+ ******************************************************************************/
+
+/* Perform any necessary steps prior to shutting down for the "NULL" module. */
+static void null_begin_shutdown()
+{
+    assert(null_runtime);
+
+    NULL_LOCK();
+
+    /* In general, we want to disable all wrappers while Darshan shuts down. 
+     * This is to avoid race conditions and ensure data consistency, as
+     * executing wrappers could potentially modify module state while Darshan
+     * is in the process of shutting down. 
+     */
+    instrumentation_disabled = 1;
+
+    /* ... any other code which needs to be executed before beginning shutdown process ... */
+
+    NULL_UNLOCK();
+
+    return;
+}
+
+/* Pass output data for the "NULL" module back to darshan-core to log to file. */
+static void null_get_output_data(
+    void **buffer,
+    int *size)
+{
+    assert(null_runtime);
+
+    /* Just set the output buffer to point at the array of the "NULL" module's
+     * I/O records, and set the output size according to the number of records
+     * currently being tracked.
+     */
+    *buffer = (void *)(null_runtime->record_array);
+    *size = null_runtime->rec_array_ndx * sizeof(struct darshan_null_record);
+
+    return;
+}
+
+/* Shutdown the "NULL" module by freeing up all data structures. */
+static void null_shutdown()
+{
+    assert(null_runtime);
+
+    HASH_CLEAR(hlink, null_runtime->record_hash); /* these hash entries are freed all at once below */
+
+    free(null_runtime->runtime_record_array);
+    free(null_runtime->record_array);
+    free(null_runtime);
+    null_runtime = NULL;
+
+    return;
+}
+
+/*
+ * Local variables:
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ * End:
+ *
+ * vim: ts=8 sts=4 sw=4 expandtab
+ */
diff --git a/darshan-runtime/lib/darshan-posix.c b/darshan-runtime/lib/darshan-posix.c
index a647f0e..c66a118 100644
--- a/darshan-runtime/lib/darshan-posix.c
+++ b/darshan-runtime/lib/darshan-posix.c
@@ -215,9 +215,9 @@ static void posix_walk_file_accesses(void);
 static int posix_access_compare(const void* a_p, const void* b_p);
 static int posix_file_compare(const void* a, const void* b);
 
-static void posix_disable_instrumentation(void);
-static void posix_prepare_for_reduction(darshan_record_id *shared_recs,
-    int *shared_rec_count, void **send_buf, void **recv_buf, int *rec_size);
+static void posix_begin_shutdown(void);
+static void posix_setup_reduction(darshan_record_id *shared_recs, int *shared_rec_count,
+    void **send_buf, void **recv_buf, int *rec_size);
 static void posix_record_reduction_op(void* infile_v, void* inoutfile_v,
     int *len, MPI_Datatype *datatype);
 static void posix_get_output_data(void **buffer, int *size);
@@ -1295,8 +1295,8 @@ static void posix_runtime_initialize()
     int mem_limit;
     struct darshan_module_funcs posix_mod_fns =
     {
-        .disable_instrumentation = &posix_disable_instrumentation,
-        .prepare_for_reduction = &posix_prepare_for_reduction,
+        .begin_shutdown = &posix_begin_shutdown,
+        .setup_reduction = &posix_setup_reduction,
         .record_reduction_op = &posix_record_reduction_op,
         .get_output_data = &posix_get_output_data,
         .shutdown = &posix_shutdown
@@ -1627,21 +1627,25 @@ static int posix_file_compare(const void* a_p, const void* b_p)
  * Functions exported by this module for coordinating with darshan-core *
  ************************************************************************/
 
-static void posix_disable_instrumentation()
+static void posix_begin_shutdown()
 {
     assert(posix_runtime);
 
     POSIX_LOCK();
 
+    /* go through file access data for each record and set the 4 most common
+     * stride/access size counters.
+     */
     posix_walk_file_accesses();
 
+    /* disable further instrumentation while Darshan shuts down */
     instrumentation_disabled = 1;
     POSIX_UNLOCK();
 
     return;
 }
 
-static void posix_prepare_for_reduction(
+static void posix_setup_reduction(
     darshan_record_id *shared_recs,
     int *shared_rec_count,
     void **send_buf,
@@ -1980,6 +1984,8 @@ static void posix_shutdown()
 {
     struct posix_file_runtime_ref *ref, *tmp;
 
+    assert(posix_runtime);
+
     HASH_ITER(hlink, posix_runtime->fd_hash, ref, tmp)
     {
         HASH_DELETE(hlink, posix_runtime->fd_hash, ref);
diff --git a/darshan-runtime/utlist.h b/darshan-runtime/utlist.h
new file mode 100644
index 0000000..35fc9db
--- /dev/null
+++ b/darshan-runtime/utlist.h
@@ -0,0 +1,490 @@
+/*
+Copyright (c) 2007-2010, Troy D. Hanson   http://uthash.sourceforge.net
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef UTLIST_H
+#define UTLIST_H
+
+#define UTLIST_VERSION 1.9.1
+
+/* 
+ * This file contains macros to manipulate singly and doubly-linked lists.
+ *
+ * 1. LL_ macros:  singly-linked lists.
+ * 2. DL_ macros:  doubly-linked lists.
+ * 3. CDL_ macros: circular doubly-linked lists.
+ *
+ * To use singly-linked lists, your structure must have a "next" pointer.
+ * To use doubly-linked lists, your structure must "prev" and "next" pointers.
+ * Either way, the pointer to the head of the list must be initialized to NULL.
+ * 
+ * ----------------.EXAMPLE -------------------------
+ * struct item {
+ *      int id;
+ *      struct item *prev, *next;
+ * }
+ *
+ * struct item *list = NULL:
+ *
+ * int main() {
+ *      struct item *item;
+ *      ... allocate and populate item ...
+ *      DL_APPEND(list, item);
+ * }
+ * --------------------------------------------------
+ *
+ * For doubly-linked lists, the append and delete macros are O(1)
+ * For singly-linked lists, append and delete are O(n) but prepend is O(1)
+ * The sort macro is O(n log(n)) for all types of single/double/circular lists.
+ */
+
+/* These macros use decltype or the earlier __typeof GNU extension.
+   As decltype is only available in newer compilers (VS2010 or gcc 4.3+
+   when compiling c++ code), this code uses whatever method is needed
+   or, for VS2008 where neither is available, uses casting workarounds. */
+#ifdef _MSC_VER            /* MS compiler */
+#if _MSC_VER >= 1600 && __cplusplus  /* VS2010 and newer in C++ mode */
+#define LDECLTYPE(x) decltype(x)
+#else                     /* VS2008 or older (or VS2010 in C mode) */
+#define NO_DECLTYPE
+#define LDECLTYPE(x) char*
+#endif
+#else                      /* GNU, Sun and other compilers */
+#define LDECLTYPE(x) __typeof(x)
+#endif
+
+/* for VS2008 we use some workarounds to get around the lack of decltype,
+ * namely, we always reassign our tmp variable to the list head if we need
+ * to dereference its prev/next pointers, and save/restore the real head.*/
+#ifdef NO_DECLTYPE
+#define _SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); }
+#define _NEXT(elt,list) ((char*)((list)->next))
+#define _NEXTASGN(elt,list,to) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); }
+#define _PREV(elt,list) ((char*)((list)->prev))
+#define _PREVASGN(elt,list,to) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); }
+#define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; }
+#define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); }
+#else 
+#define _SV(elt,list)
+#define _NEXT(elt,list) ((elt)->next)
+#define _NEXTASGN(elt,list,to) ((elt)->next)=(to)
+#define _PREV(elt,list) ((elt)->prev)
+#define _PREVASGN(elt,list,to) ((elt)->prev)=(to)
+#define _RS(list)
+#define _CASTASGN(a,b) (a)=(b)
+#endif
+
+/******************************************************************************
+ * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort    *
+ * Unwieldy variable names used here to avoid shadowing passed-in variables.  *
+ *****************************************************************************/
+#define LL_SORT(list, cmp)                                                                     \
+do {                                                                                           \
+  LDECLTYPE(list) _ls_p;                                                                       \
+  LDECLTYPE(list) _ls_q;                                                                       \
+  LDECLTYPE(list) _ls_e;                                                                       \
+  LDECLTYPE(list) _ls_tail;                                                                    \
+  LDECLTYPE(list) _ls_oldhead;                                                                 \
+  LDECLTYPE(list) _tmp;                                                                        \
+  int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping;                       \
+  if (list) {                                                                                  \
+    _ls_insize = 1;                                                                            \
+    _ls_looping = 1;                                                                           \
+    while (_ls_looping) {                                                                      \
+      _CASTASGN(_ls_p,list);                                                                   \
+      _CASTASGN(_ls_oldhead,list);                                                             \
+      list = NULL;                                                                             \
+      _ls_tail = NULL;                                                                         \
+      _ls_nmerges = 0;                                                                         \
+      while (_ls_p) {                                                                          \
+        _ls_nmerges++;                                                                         \
+        _ls_q = _ls_p;                                                                         \
+        _ls_psize = 0;                                                                         \
+        for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) {                                         \
+          _ls_psize++;                                                                         \
+          _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list);                               \
+          if (!_ls_q) break;                                                                   \
+        }                                                                                      \
+        _ls_qsize = _ls_insize;                                                                \
+        while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) {                                    \
+          if (_ls_psize == 0) {                                                                \
+            _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \
+          } else if (_ls_qsize == 0 || !_ls_q) {                                               \
+            _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \
+          } else if (cmp(_ls_p,_ls_q) <= 0) {                                                  \
+            _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \
+          } else {                                                                             \
+            _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \
+          }                                                                                    \
+          if (_ls_tail) {                                                                      \
+            _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list);                     \
+          } else {                                                                             \
+            _CASTASGN(list,_ls_e);                                                             \
+          }                                                                                    \
+          _ls_tail = _ls_e;                                                                    \
+        }                                                                                      \
+        _ls_p = _ls_q;                                                                         \
+      }                                                                                        \
+      _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL); _RS(list);                            \
+      if (_ls_nmerges <= 1) {                                                                  \
+        _ls_looping=0;                                                                         \
+      }                                                                                        \
+      _ls_insize *= 2;                                                                         \
+    }                                                                                          \
+  } else _tmp=NULL; /* quiet gcc unused variable warning */                                    \
+} while (0)
+
+#define DL_SORT(list, cmp)                                                                     \
+do {                                                                                           \
+  LDECLTYPE(list) _ls_p;                                                                       \
+  LDECLTYPE(list) _ls_q;                                                                       \
+  LDECLTYPE(list) _ls_e;                                                                       \
+  LDECLTYPE(list) _ls_tail;                                                                    \
+  LDECLTYPE(list) _ls_oldhead;                                                                 \
+  LDECLTYPE(list) _tmp;                                                                        \
+  int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping;                       \
+  if (list) {                                                                                  \
+    _ls_insize = 1;                                                                            \
+    _ls_looping = 1;                                                                           \
+    while (_ls_looping) {                                                                      \
+      _CASTASGN(_ls_p,list);                                                                   \
+      _CASTASGN(_ls_oldhead,list);                                                             \
+      list = NULL;                                                                             \
+      _ls_tail = NULL;                                                                         \
+      _ls_nmerges = 0;                                                                         \
+      while (_ls_p) {                                                                          \
+        _ls_nmerges++;                                                                         \
+        _ls_q = _ls_p;                                                                         \
+        _ls_psize = 0;                                                                         \
+        for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) {                                         \
+          _ls_psize++;                                                                         \
+          _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list);                               \
+          if (!_ls_q) break;                                                                   \
+        }                                                                                      \
+        _ls_qsize = _ls_insize;                                                                \
+        while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) {                                    \
+          if (_ls_psize == 0) {                                                                \
+            _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \
+          } else if (_ls_qsize == 0 || !_ls_q) {                                               \
+            _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \
+          } else if (cmp(_ls_p,_ls_q) <= 0) {                                                  \
+            _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \
+          } else {                                                                             \
+            _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \
+          }                                                                                    \
+          if (_ls_tail) {                                                                      \
+            _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list);                     \
+          } else {                                                                             \
+            _CASTASGN(list,_ls_e);                                                             \
+          }                                                                                    \
+          _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail); _RS(list);                          \
+          _ls_tail = _ls_e;                                                                    \
+        }                                                                                      \
+        _ls_p = _ls_q;                                                                         \
+      }                                                                                        \
+      _CASTASGN(list->prev, _ls_tail);                                                         \
+      _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL); _RS(list);                            \
+      if (_ls_nmerges <= 1) {                                                                  \
+        _ls_looping=0;                                                                         \
+      }                                                                                        \
+      _ls_insize *= 2;                                                                         \
+    }                                                                                          \
+  } else _tmp=NULL; /* quiet gcc unused variable warning */                                    \
+} while (0)
+
+#define CDL_SORT(list, cmp)                                                                    \
+do {                                                                                           \
+  LDECLTYPE(list) _ls_p;                                                                       \
+  LDECLTYPE(list) _ls_q;                                                                       \
+  LDECLTYPE(list) _ls_e;                                                                       \
+  LDECLTYPE(list) _ls_tail;                                                                    \
+  LDECLTYPE(list) _ls_oldhead;                                                                 \
+  LDECLTYPE(list) _tmp;                                                                        \
+  LDECLTYPE(list) _tmp2;                                                                       \
+  int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping;                       \
+  if (list) {                                                                                  \
+    _ls_insize = 1;                                                                            \
+    _ls_looping = 1;                                                                           \
+    while (_ls_looping) {                                                                      \
+      _CASTASGN(_ls_p,list);                                                                   \
+      _CASTASGN(_ls_oldhead,list);                                                             \
+      list = NULL;                                                                             \
+      _ls_tail = NULL;                                                                         \
+      _ls_nmerges = 0;                                                                         \
+      while (_ls_p) {                                                                          \
+        _ls_nmerges++;                                                                         \
+        _ls_q = _ls_p;                                                                         \
+        _ls_psize = 0;                                                                         \
+        for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) {                                         \
+          _ls_psize++;                                                                         \
+          _SV(_ls_q,list);                                                                     \
+          if (_NEXT(_ls_q,list) == _ls_oldhead) {                                              \
+            _ls_q = NULL;                                                                      \
+          } else {                                                                             \
+            _ls_q = _NEXT(_ls_q,list);                                                         \
+          }                                                                                    \
+          _RS(list);                                                                           \
+          if (!_ls_q) break;                                                                   \
+        }                                                                                      \
+        _ls_qsize = _ls_insize;                                                                \
+        while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) {                                    \
+          if (_ls_psize == 0) {                                                                \
+            _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \
+            if (_ls_q == _ls_oldhead) { _ls_q = NULL; }                                        \
+          } else if (_ls_qsize == 0 || !_ls_q) {                                               \
+            _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \
+            if (_ls_p == _ls_oldhead) { _ls_p = NULL; }                                        \
+          } else if (cmp(_ls_p,_ls_q) <= 0) {                                                  \
+            _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = _NEXT(_ls_p,list); _RS(list); _ls_psize--; \
+            if (_ls_p == _ls_oldhead) { _ls_p = NULL; }                                        \
+          } else {                                                                             \
+            _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list); _RS(list); _ls_qsize--; \
+            if (_ls_q == _ls_oldhead) { _ls_q = NULL; }                                        \
+          }                                                                                    \
+          if (_ls_tail) {                                                                      \
+            _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e); _RS(list);                     \
+          } else {                                                                             \
+            _CASTASGN(list,_ls_e);                                                             \
+          }                                                                                    \
+          _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail); _RS(list);                          \
+          _ls_tail = _ls_e;                                                                    \
+        }                                                                                      \
+        _ls_p = _ls_q;                                                                         \
+      }                                                                                        \
+      _CASTASGN(list->prev,_ls_tail);                                                          \
+      _CASTASGN(_tmp2,list);                                                                   \
+      _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_tmp2); _RS(list);                           \
+      if (_ls_nmerges <= 1) {                                                                  \
+        _ls_looping=0;                                                                         \
+      }                                                                                        \
+      _ls_insize *= 2;                                                                         \
+    }                                                                                          \
+  } else _tmp=NULL; /* quiet gcc unused variable warning */                                    \
+} while (0)
+
+/******************************************************************************
+ * singly linked list macros (non-circular)                                   *
+ *****************************************************************************/
+#define LL_PREPEND(head,add)                                                                   \
+do {                                                                                           \
+  (add)->next = head;                                                                          \
+  head = add;                                                                                  \
+} while (0)
+
+#define LL_APPEND(head,add)                                                                    \
+do {                                                                                           \
+  LDECLTYPE(head) _tmp;                                                                        \
+  (add)->next=NULL;                                                                            \
+  if (head) {                                                                                  \
+    _tmp = head;                                                                               \
+    while (_tmp->next) { _tmp = _tmp->next; }                                                  \
+    _tmp->next=(add);                                                                          \
+  } else {                                                                                     \
+    (head)=(add);                                                                              \
+  }                                                                                            \
+} while (0)
+
+#define LL_DELETE(head,del)                                                                    \
+do {                                                                                           \
+  LDECLTYPE(head) _tmp;                                                                        \
+  if ((head) == (del)) {                                                                       \
+    (head)=(head)->next;                                                                       \
+  } else {                                                                                     \
+    _tmp = head;                                                                               \
+    while (_tmp->next && (_tmp->next != (del))) {                                              \
+      _tmp = _tmp->next;                                                                       \
+    }                                                                                          \
+    if (_tmp->next) {                                                                          \
+      _tmp->next = ((del)->next);                                                              \
+    }                                                                                          \
+  }                                                                                            \
+} while (0)
+
+/* Here are VS2008 replacements for LL_APPEND and LL_DELETE */
+#define LL_APPEND_VS2008(head,add)                                                             \
+do {                                                                                           \
+  if (head) {                                                                                  \
+    (add)->next = head;     /* use add->next as a temp variable */                             \
+    while ((add)->next->next) { (add)->next = (add)->next->next; }                             \
+    (add)->next->next=(add);                                                                   \
+  } else {                                                                                     \
+    (head)=(add);                                                                              \
+  }                                                                                            \
+  (add)->next=NULL;                                                                            \
+} while (0)
+
+#define LL_DELETE_VS2008(head,del)                                                             \
+do {                                                                                           \
+  if ((head) == (del)) {                                                                       \
+    (head)=(head)->next;                                                                       \
+  } else {                                                                                     \
+    char *_tmp = (char*)(head);                                                                \
+    while (head->next && (head->next != (del))) {                                              \
+      head = head->next;                                                                       \
+    }                                                                                          \
+    if (head->next) {                                                                          \
+      head->next = ((del)->next);                                                              \
+    }                                                                                          \
+    {                                                                                          \
+      char **_head_alias = (char**)&(head);                                                    \
+      *_head_alias = _tmp;                                                                     \
+    }                                                                                          \
+  }                                                                                            \
+} while (0)
+#ifdef NO_DECLTYPE
+#undef LL_APPEND
+#define LL_APPEND LL_APPEND_VS2008
+#undef LL_DELETE
+#define LL_DELETE LL_DELETE_VS2008
+#endif
+/* end VS2008 replacements */
+
+#define LL_FOREACH(head,el)                                                                    \
+    for(el=head;el;el=el->next)
+
+#define LL_FOREACH_SAFE(head,el,tmp)                                                           \
+  for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp)
+
+#define LL_SEARCH_SCALAR(head,out,field,val)                                                   \
+do {                                                                                           \
+    LL_FOREACH(head,out) {                                                                     \
+      if ((out)->field == (val)) break;                                                        \
+    }                                                                                          \
+} while(0) 
+
+#define LL_SEARCH(head,out,elt,cmp)                                                            \
+do {                                                                                           \
+    LL_FOREACH(head,out) {                                                                     \
+      if ((cmp(out,elt))==0) break;                                                            \
+    }                                                                                          \
+} while(0) 
+
+/******************************************************************************
+ * doubly linked list macros (non-circular)                                   *
+ *****************************************************************************/
+#define DL_PREPEND(head,add)                                                                   \
+do {                                                                                           \
+ (add)->next = head;                                                                           \
+ if (head) {                                                                                   \
+   (add)->prev = (head)->prev;                                                                 \
+   (head)->prev = (add);                                                                       \
+ } else {                                                                                      \
+   (add)->prev = (add);                                                                        \
+ }                                                                                             \
+ (head) = (add);                                                                               \
+} while (0)
+
+#define DL_APPEND(head,add)                                                                    \
+do {                                                                                           \
+  if (head) {                                                                                  \
+      (add)->prev = (head)->prev;                                                              \
+      (head)->prev->next = (add);                                                              \
+      (head)->prev = (add);                                                                    \
+      (add)->next = NULL;                                                                      \
+  } else {                                                                                     \
+      (head)=(add);                                                                            \
+      (head)->prev = (head);                                                                   \
+      (head)->next = NULL;                                                                     \
+  }                                                                                            \
+} while (0);
+
+#define DL_DELETE(head,del)                                                                    \
+do {                                                                                           \
+  if ((del)->prev == (del)) {                                                                  \
+      (head)=NULL;                                                                             \
+  } else if ((del)==(head)) {                                                                  \
+      (del)->next->prev = (del)->prev;                                                         \
+      (head) = (del)->next;                                                                    \
+  } else {                                                                                     \
+      (del)->prev->next = (del)->next;                                                         \
+      if ((del)->next) {                                                                       \
+          (del)->next->prev = (del)->prev;                                                     \
+      } else {                                                                                 \
+          (head)->prev = (del)->prev;                                                          \
+      }                                                                                        \
+  }                                                                                            \
+} while (0);
+
+
+#define DL_FOREACH(head,el)                                                                    \
+    for(el=head;el;el=el->next)
+
+/* this version is safe for deleting the elements during iteration */
+#define DL_FOREACH_SAFE(head,el,tmp)                                                           \
+  for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp)
+
+/* these are identical to their singly-linked list counterparts */
+#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR
+#define DL_SEARCH LL_SEARCH
+
+/******************************************************************************
+ * circular doubly linked list macros                                         *
+ *****************************************************************************/
+#define CDL_PREPEND(head,add)                                                                  \
+do {                                                                                           \
+ if (head) {                                                                                   \
+   (add)->prev = (head)->prev;                                                                 \
+   (add)->next = (head);                                                                       \
+   (head)->prev = (add);                                                                       \
+   (add)->prev->next = (add);                                                                  \
+ } else {                                                                                      \
+   (add)->prev = (add);                                                                        \
+   (add)->next = (add);                                                                        \
+ }                                                                                             \
+(head)=(add);                                                                                  \
+} while (0)
+
+#define CDL_DELETE(head,del)                                                                   \
+do {                                                                                           \
+  if ( ((head)==(del)) && ((head)->next == (head))) {                                          \
+      (head) = 0L;                                                                             \
+  } else {                                                                                     \
+     (del)->next->prev = (del)->prev;                                                          \
+     (del)->prev->next = (del)->next;                                                          \
+     if ((del) == (head)) (head)=(del)->next;                                                  \
+  }                                                                                            \
+} while (0);
+
+#define CDL_FOREACH(head,el)                                                                   \
+    for(el=head;el;el=(el->next==head ? 0L : el->next)) 
+
+#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2)                                                    \
+  for((el)=(head), ((tmp1)=(head)?((head)->prev):NULL);                                        \
+      (el) && ((tmp2)=(el)->next, 1);                                                          \
+      ((el) = (((el)==(tmp1)) ? 0L : (tmp2))))
+
+#define CDL_SEARCH_SCALAR(head,out,field,val)                                                  \
+do {                                                                                           \
+    CDL_FOREACH(head,out) {                                                                    \
+      if ((out)->field == (val)) break;                                                        \
+    }                                                                                          \
+} while(0) 
+
+#define CDL_SEARCH(head,out,elt,cmp)                                                           \
+do {                                                                                           \
+    CDL_FOREACH(head,out) {                                                                    \
+      if ((cmp(out,elt))==0) break;                                                            \
+    }                                                                                          \
+} while(0) 
+
+#endif /* UTLIST_H */
+
diff --git a/doc/darshan-modularization.txt b/doc/darshan-modularization.txt
index 40a70f6..693e55a 100644
--- a/doc/darshan-modularization.txt
+++ b/doc/darshan-modularization.txt
@@ -6,11 +6,6 @@ Darshan modularization branch development notes
 Darshan is a lightweight toolkit for characterizing the I/O performance of instrumented
 HPC applications.
 
-Darshan was originally designed to gather I/O data from a static set of sources.
-Adding instrumentation for additional sources of I/O data was only possible through
-manual modification of the Darshan log file format, which consequentially breaks
-any other utilities reliant on that format.
-
 Starting with version 3.0.0, the Darshan runtime environment and log file format have
 been redesigned such that new "instrumentation modules" can be added without breaking
 existing tools. Developers are given a framework to implement arbitrary instrumentation
@@ -52,6 +47,8 @@ applications and generating I/O characterization logs.
 * *darshan-util*: Darshan utilities for analyzing the contents of a given Darshan
 I/O characterization log.
 
+
+
 The following subsections provide an overview of each of these components with specific
 attention to how new instrumentation modules may be integrated into Darshan.
 


hooks/post-receive
--



More information about the Darshan-commits mailing list