[Darshan-commits] [Darshan] branch, dev-modular, updated. darshan-2.3.1-125-g158337c

Service Account git at mcs.anl.gov
Tue Aug 4 16:51:44 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  158337c98e18596458cf2b3102c3f4a63c2ebfae (commit)
       via  b6807fcc3b14ebb5e1323e219a72ddbfde2af64e (commit)
       via  019fba2263b7ab66fecde7298f4c6dd84a4e1e2f (commit)
       via  dafe379082b24c552f1285c30a3fcf4edd8db8de (commit)
       via  f1468f4943f8bba74afbd2a3f84ce6fc0071a556 (commit)
       via  8eea697a4c9e6eaca6cc6021b29521bf23e9c3d3 (commit)
      from  a0b3f9cc1820fa2e7828ad0919886e9739e03187 (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 158337c98e18596458cf2b3102c3f4a63c2ebfae
Author: Shane Snyder <ssnyder at mcs.anl.gov>
Date:   Tue Aug 4 16:51:02 2015 -0500

    add code for the pnetcdf module

commit b6807fcc3b14ebb5e1323e219a72ddbfde2af64e
Author: Shane Snyder <ssnyder at mcs.anl.gov>
Date:   Tue Aug 4 14:31:56 2015 -0500

    forgot hdf5 log format header

commit 019fba2263b7ab66fecde7298f4c6dd84a4e1e2f
Author: Shane Snyder <ssnyder at mcs.anl.gov>
Date:   Tue Aug 4 13:42:00 2015 -0500

    tiny misc. edits

commit dafe379082b24c552f1285c30a3fcf4edd8db8de
Author: Shane Snyder <ssnyder at mcs.anl.gov>
Date:   Tue Aug 4 13:41:29 2015 -0500

    fill in the hdf5 module

commit f1468f4943f8bba74afbd2a3f84ce6fc0071a556
Author: Shane Snyder <ssnyder at mcs.anl.gov>
Date:   Tue Aug 4 10:13:23 2015 -0500

    stubbed out version of hdf5 module

commit 8eea697a4c9e6eaca6cc6021b29521bf23e9c3d3
Author: Shane Snyder <ssnyder at mcs.anl.gov>
Date:   Fri Jul 24 16:09:33 2015 -0500

    refactor logutil code, create base darshan-parser

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

Summary of changes:
 darshan-hdf5-log-format.h             |   56 ++
 darshan-pnetcdf-log-format.h          |   58 ++
 darshan-runtime/Makefile.in           |   21 +-
 darshan-runtime/lib/darshan-hdf5.c    |  576 +++++++++++++++++
 darshan-runtime/lib/darshan-mpiio.c   |    4 +-
 darshan-runtime/lib/darshan-pnetcdf.c |  588 ++++++++++++++++++
 darshan-runtime/lib/darshan-posix.c   |    9 +-
 darshan-util/Makefile.in              |   20 +-
 darshan-util/darshan-logutils.c       |   43 +-
 darshan-util/darshan-logutils.h       |   57 ++-
 darshan-util/darshan-mpiio-logutils.c |   43 ++-
 darshan-util/darshan-mpiio-logutils.h |   17 +-
 darshan-util/darshan-mpiio-parser.c   |  200 ------
 darshan-util/darshan-parser.c         | 1095 ++++-----------------------------
 darshan-util/darshan-posix-logutils.c |   43 ++-
 darshan-util/darshan-posix-logutils.h |   17 +-
 darshan-util/darshan-posix-parser.c   |  195 ------
 17 files changed, 1579 insertions(+), 1463 deletions(-)
 create mode 100644 darshan-hdf5-log-format.h
 create mode 100644 darshan-pnetcdf-log-format.h
 create mode 100644 darshan-runtime/lib/darshan-hdf5.c
 create mode 100644 darshan-runtime/lib/darshan-pnetcdf.c
 delete mode 100644 darshan-util/darshan-mpiio-parser.c
 delete mode 100644 darshan-util/darshan-posix-parser.c


Diff of changes:
diff --git a/darshan-hdf5-log-format.h b/darshan-hdf5-log-format.h
new file mode 100644
index 0000000..1a67092
--- /dev/null
+++ b/darshan-hdf5-log-format.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2015 University of Chicago.
+ * See COPYRIGHT notice in top-level directory.
+ *
+ */
+
+#ifndef __DARSHAN_HDF5_LOG_FORMAT_H
+#define __DARSHAN_HDF5_LOG_FORMAT_H
+
+#include "darshan-log-format.h"
+
+#define HDF5_COUNTERS \
+    /* count of HDF5 opens */\
+    X(HDF5_OPENS) \
+    /* end of counters */\
+    X(HDF5_NUM_INDICES)
+
+#define HDF5_F_COUNTERS \
+    /* timestamp of first open */\
+    X(HDF5_F_OPEN_TIMESTAMP) \
+    /* timestamp of last close */\
+    X(HDF5_F_CLOSE_TIMESTAMP) \
+    /* end of counters*/\
+    X(HDF5_F_NUM_INDICES)
+
+#define X(a) a,
+/* integer statistics for HDF5 file records */
+enum darshan_hdf5_indices
+{
+    HDF5_COUNTERS
+};
+
+/* floating point statistics for HDF5 file records */
+enum darshan_hdf5_f_indices
+{
+    HDF5_F_COUNTERS
+};
+#undef X
+
+/* file record structure for HDF5 files. a record is created and stored for
+ * every HDF5 file opened by the original application. For the HDF5 module,
+ * the record includes:
+ *      - a corresponding record identifier (created by hashing the file path)
+ *      - the rank of the process which opened the file (-1 for shared files)
+ *      - integer file I/O statistics (open, read/write counts, etc)
+ *      - floating point I/O statistics (timestamps, cumulative timers, etc.)
+ */
+struct darshan_hdf5_file
+{
+    darshan_record_id f_id;
+    int64_t rank;
+    int64_t counters[HDF5_NUM_INDICES];
+    double fcounters[HDF5_F_NUM_INDICES];
+};
+
+#endif /* __DARSHAN_HDF5_LOG_FORMAT_H */
diff --git a/darshan-pnetcdf-log-format.h b/darshan-pnetcdf-log-format.h
new file mode 100644
index 0000000..8bb3565
--- /dev/null
+++ b/darshan-pnetcdf-log-format.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 University of Chicago.
+ * See COPYRIGHT notice in top-level directory.
+ *
+ */
+
+#ifndef __DARSHAN_PNETCDF_LOG_FORMAT_H
+#define __DARSHAN_PNETCDF_LOG_FORMAT_H
+
+#include "darshan-log-format.h"
+
+#define PNETCDF_COUNTERS \
+    /* count of PNETCDF independent opens */\
+    X(PNETCDF_INDEP_OPENS) \
+    /* count of PNETCDF collective opens */\
+    X(PNETCDF_COLL_OPENS) \
+    /* end of counters */\
+    X(PNETCDF_NUM_INDICES)
+
+#define PNETCDF_F_COUNTERS \
+    /* timestamp of first open */\
+    X(PNETCDF_F_OPEN_TIMESTAMP) \
+    /* timestamp of last close */\
+    X(PNETCDF_F_CLOSE_TIMESTAMP) \
+    /* end of counters*/\
+    X(PNETCDF_F_NUM_INDICES)
+
+#define X(a) a,
+/* integer statistics for PNETCDF file records */
+enum darshan_pnetcdf_indices
+{
+    PNETCDF_COUNTERS
+};
+
+/* floating point statistics for PNETCDF file records */
+enum darshan_pnetcdf_f_indices
+{
+    PNETCDF_F_COUNTERS
+};
+#undef X
+
+/* file record structure for PNETCDF files. a record is created and stored for
+ * every PNETCDF file opened by the original application. For the PNETCDF module,
+ * the record includes:
+ *      - a corresponding record identifier (created by hashing the file path)
+ *      - the rank of the process which opened the file (-1 for shared files)
+ *      - integer file I/O statistics (open, read/write counts, etc)
+ *      - floating point I/O statistics (timestamps, cumulative timers, etc.)
+ */
+struct darshan_pnetcdf_file
+{
+    darshan_record_id f_id;
+    int64_t rank;
+    int64_t counters[PNETCDF_NUM_INDICES];
+    double fcounters[PNETCDF_F_NUM_INDICES];
+};
+
+#endif /* __DARSHAN_PNETCDF_LOG_FORMAT_H */
diff --git a/darshan-runtime/Makefile.in b/darshan-runtime/Makefile.in
index be3485b..65c9f50 100644
--- a/darshan-runtime/Makefile.in
+++ b/darshan-runtime/Makefile.in
@@ -25,7 +25,7 @@ endif
 VPATH = $(srcdir)
 
 CFLAGS = -DDARSHAN_CONFIG_H=\"darshan-runtime-config.h\" -I . -I ../ -I $(srcdir) -I$(srcdir)/../ @CFLAGS@ @CPPFLAGS@ -D_LARGEFILE64_SOURCE 
-#
+
 CFLAGS_SHARED = -DDARSHAN_CONFIG_H=\"darshan-runtime-config.h\" -I . -I$(srcdir) -I$(srcdir)/../ @CFLAGS@ @CPPFLAGS@ -D_LARGEFILE64_SOURCE -shared -fpic -DPIC -DDARSHAN_PRELOAD
 
 LIBS = -lz @LIBBZ2@
@@ -69,6 +69,18 @@ lib/darshan-mpiio.o: lib/darshan-mpiio.c darshan.h $(DARSHAN_LOG_FORMAT) $(srcdi
 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/darshan-hdf5.o: lib/darshan-hdf5.c darshan.h $(DARSHAN_LOG_FORMAT) $(srcdir)/../darshan-hdf5-log-format.h | lib
+	$(CC) $(CFLAGS) -c $< -o $@
+
+lib/darshan-hdf5.po: lib/darshan-hdf5.c darshan.h $(DARSHAN_LOG_FORMAT) $(srcdir)/../darshan-hdf5-log-format.h | lib
+	$(CC) $(CFLAGS_SHARED) -c $< -o $@
+
+lib/darshan-pnetcdf.o: lib/darshan-pnetcdf.c darshan.h $(DARSHAN_LOG_FORMAT) $(srcdir)/../darshan-pnetcdf-log-format.h | lib
+	$(CC) $(CFLAGS) -c $< -o $@
+
+lib/darshan-pnetcdf.po: lib/darshan-pnetcdf.c darshan.h $(DARSHAN_LOG_FORMAT) $(srcdir)/../darshan-pnetcdf-log-format.h | lib
+	$(CC) $(CFLAGS_SHARED) -c $< -o $@
+
 lib/lookup3.o: lib/lookup3.c
 	$(CC) $(CFLAGS) -c $< -o $@
 
@@ -81,13 +93,10 @@ lib/lookup8.o: lib/lookup8.c
 lib/lookup8.po: lib/lookup8.c
 	$(CC) $(CFLAGS_SHARED) -c $< -o $@
 
-#%.i: %.c
-#	$(CC) -E $(CFLAGS) -c $< -o $@
-
-lib/libdarshan.a: lib/darshan-core-init-finalize.o lib/darshan-core.o lib/darshan-common.o lib/darshan-posix.o lib/darshan-mpiio.o lib/lookup3.o lib/lookup8.o
+lib/libdarshan.a: lib/darshan-core-init-finalize.o lib/darshan-core.o lib/darshan-common.o lib/darshan-posix.o lib/darshan-mpiio.o lib/darshan-hdf5.o lib/darshan-pnetcdf.o lib/lookup3.o lib/lookup8.o
 	ar rcs $@ $^
 
-lib/libdarshan.so: lib/darshan-core-init-finalize.po lib/darshan-core.po lib/darshan-common.po lib/darshan-posix.po lib/darshan-mpiio.po lib/lookup3.po lib/lookup8.po
+lib/libdarshan.so: lib/darshan-core-init-finalize.po lib/darshan-core.po lib/darshan-common.po lib/darshan-posix.po lib/darshan-mpiio.po lib/darshan-hdf5.po lib/darshan-pnetcdf.po lib/lookup3.po lib/lookup8.po
 	$(CC) $(CFLAGS_SHARED) $(LDFLAGS) -o $@ $^ -lpthread -lrt -lz -ldl
 
 install:: all
diff --git a/darshan-runtime/lib/darshan-hdf5.c b/darshan-runtime/lib/darshan-hdf5.c
new file mode 100644
index 0000000..b94010a
--- /dev/null
+++ b/darshan-runtime/lib/darshan-hdf5.c
@@ -0,0 +1,576 @@
+/*
+ * Copyright (C) 2015 University of Chicago.
+ * See COPYRIGHT notice in top-level directory.
+ *
+ */
+
+#include "darshan-runtime-config.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdarg.h>
+#include <string.h>
+#include <time.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <search.h>
+#include <assert.h>
+#define __USE_GNU
+#include <pthread.h>
+
+#include "uthash.h"
+
+#include "darshan.h"
+#include "darshan-hdf5-log-format.h"
+#include "darshan-dynamic.h"
+
+/* hope this doesn't change any time soon */
+typedef int hid_t;
+typedef int herr_t;
+
+DARSHAN_FORWARD_DECL(H5Fcreate, hid_t, (const char *filename, unsigned flags, hid_t create_plist, hid_t access_plist));
+DARSHAN_FORWARD_DECL(H5Fopen, hid_t, (const char *filename, unsigned flags, hid_t access_plist));
+DARSHAN_FORWARD_DECL(H5Fclose, herr_t, (hid_t file_id));
+
+/* structure to track i/o stats for a given hdf5 file at runtime */
+struct hdf5_file_runtime
+{
+    struct darshan_hdf5_file* file_record;
+    UT_hash_handle hlink;
+};
+
+/* structure to associate a HDF5 hid with an existing file runtime structure */
+struct hdf5_file_runtime_ref
+{
+    struct hdf5_file_runtime* file;
+    hid_t hid;
+    UT_hash_handle hlink;
+};
+
+/* necessary state for storing HDF5 file records and coordinating with
+ * darshan-core at shutdown time
+ */
+struct hdf5_runtime
+{
+    struct hdf5_file_runtime* file_runtime_array;
+    struct darshan_hdf5_file* file_record_array;
+    int file_array_size;
+    int file_array_ndx;
+    struct hdf5_file_runtime *file_hash;
+    struct hdf5_file_runtime_ref* hid_hash;
+};
+
+static struct hdf5_runtime *hdf5_runtime = NULL;
+static pthread_mutex_t hdf5_runtime_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+static int instrumentation_disabled = 0;
+static int my_rank = -1;
+
+static void hdf5_runtime_initialize(void);
+static struct hdf5_file_runtime* hdf5_file_by_name(const char *name);
+static struct hdf5_file_runtime* hdf5_file_by_name_sethid(const char* name, hid_t hid);
+static struct hdf5_file_runtime* hdf5_file_by_hid(hid_t hid);
+static void hdf5_file_close_hid(hid_t hid);
+static int hdf5_record_compare(const void* a, const void* b);
+static void hdf5_record_reduction_op(void* infile_v, void* inoutfile_v,
+    int *len, MPI_Datatype *datatype);
+
+static void hdf5_begin_shutdown(void);
+static void hdf5_get_output_data(MPI_Comm mod_comm, darshan_record_id *shared_recs,
+    int shared_rec_count, void **hdf5_buf, int *hdf5_buf_sz);
+static void hdf5_shutdown(void);
+
+#define HDF5_LOCK() pthread_mutex_lock(&hdf5_runtime_mutex)
+#define HDF5_UNLOCK() pthread_mutex_unlock(&hdf5_runtime_mutex)
+
+/*********************************************************
+ *        Wrappers for HDF5 functions of interest        * 
+ *********************************************************/
+
+hid_t DARSHAN_DECL(H5Fcreate)(const char *filename, unsigned flags,
+    hid_t create_plist, hid_t access_plist)
+{
+    int ret;
+    struct hdf5_file_runtime* file;
+    char* tmp;
+    double tm1;
+
+    MAP_OR_FAIL(H5Fcreate);
+
+    tm1 = darshan_core_wtime();
+    ret = __real_H5Fcreate(filename, flags, create_plist, access_plist);
+    if(ret >= 0)
+    {
+        /* use ROMIO approach to strip prefix if present */
+        /* strip off prefix if there is one, but only skip prefixes
+         * if they are greater than length one to allow for windows
+         * drive specifications (e.g. c:\...) 
+         */
+        tmp = strchr(filename, ':');
+        if (tmp > filename + 1) {
+            filename = tmp + 1;
+        }
+
+        HDF5_LOCK();
+        hdf5_runtime_initialize();
+        file = hdf5_file_by_name_sethid(filename, ret);
+        if(file)
+        {
+            if(file->file_record->fcounters[HDF5_F_OPEN_TIMESTAMP] == 0)
+                file->file_record->fcounters[HDF5_F_OPEN_TIMESTAMP] = tm1;
+            file->file_record->counters[HDF5_OPENS] += 1;
+        }
+        HDF5_UNLOCK();
+    }
+
+    return(ret);
+}
+
+hid_t DARSHAN_DECL(H5Fopen)(const char *filename, unsigned flags,
+    hid_t access_plist)
+{
+    int ret;
+    struct hdf5_file_runtime* file;
+    char* tmp;
+    double tm1;
+
+    MAP_OR_FAIL(H5Fopen);
+
+    tm1 = darshan_core_wtime();
+    ret = __real_H5Fopen(filename, flags, access_plist);
+    if(ret >= 0)
+    {
+        /* use ROMIO approach to strip prefix if present */
+        /* strip off prefix if there is one, but only skip prefixes
+         * if they are greater than length one to allow for windows
+         * drive specifications (e.g. c:\...) 
+         */
+        tmp = strchr(filename, ':');
+        if (tmp > filename + 1) {
+            filename = tmp + 1;
+        }
+
+        HDF5_LOCK();
+        hdf5_runtime_initialize();
+        file = hdf5_file_by_name_sethid(filename, ret);
+        if(file)
+        {
+            if(file->file_record->fcounters[HDF5_F_OPEN_TIMESTAMP] == 0)
+                file->file_record->fcounters[HDF5_F_OPEN_TIMESTAMP] = tm1;
+            file->file_record->counters[HDF5_OPENS] += 1;
+        }
+        HDF5_UNLOCK();
+    }
+
+    return(ret);
+
+}
+
+herr_t DARSHAN_DECL(H5Fclose)(hid_t file_id)
+{
+    struct hdf5_file_runtime* file;
+    int ret;
+
+    MAP_OR_FAIL(H5Fclose);
+
+    ret = __real_H5Fclose(file_id);
+
+    HDF5_LOCK();
+    hdf5_runtime_initialize();
+    file = hdf5_file_by_hid(file_id);
+    if(file)
+    {
+        file->file_record->fcounters[HDF5_F_CLOSE_TIMESTAMP] =
+            darshan_core_wtime();
+        hdf5_file_close_hid(file_id);
+    }
+    HDF5_UNLOCK();
+
+    return(ret);
+
+}
+
+/*********************************************************
+ * Internal functions for manipulating HDF5 module state *
+ *********************************************************/
+
+/* initialize internal HDF5 module data strucutres and register with darshan-core */
+static void hdf5_runtime_initialize()
+{
+    int mem_limit;
+    struct darshan_module_funcs hdf5_mod_fns =
+    {
+        .begin_shutdown = &hdf5_begin_shutdown,
+        .get_output_data = &hdf5_get_output_data,
+        .shutdown = &hdf5_shutdown
+    };
+
+    /* don't do anything if already initialized or instrumenation is disabled */
+    if(hdf5_runtime || instrumentation_disabled)
+        return;
+
+    /* register hdf5 module with darshan-core */
+    darshan_core_register_module(
+        DARSHAN_HDF5_MOD,
+        &hdf5_mod_fns,
+        &my_rank,
+        &mem_limit,
+        NULL);
+
+    /* return if no memory assigned by darshan-core */
+    if(mem_limit == 0)
+        return;
+
+    hdf5_runtime = malloc(sizeof(*hdf5_runtime));
+    if(!hdf5_runtime)
+        return;
+    memset(hdf5_runtime, 0, sizeof(*hdf5_runtime));
+
+    /* set maximum number of file records according to max memory limit */
+    /* NOTE: maximum number of records is based on the size of a hdf5 file record */
+    /* TODO: should we base memory usage off file record or total runtime structure sizes? */
+    hdf5_runtime->file_array_size = mem_limit / sizeof(struct darshan_hdf5_file);
+    hdf5_runtime->file_array_ndx = 0;
+
+    /* allocate array of runtime file records */
+    hdf5_runtime->file_runtime_array = malloc(hdf5_runtime->file_array_size *
+                                              sizeof(struct hdf5_file_runtime));
+    hdf5_runtime->file_record_array = malloc(hdf5_runtime->file_array_size *
+                                             sizeof(struct darshan_hdf5_file));
+    if(!hdf5_runtime->file_runtime_array || !hdf5_runtime->file_record_array)
+    {
+        hdf5_runtime->file_array_size = 0;
+        return;
+    }
+    memset(hdf5_runtime->file_runtime_array, 0, hdf5_runtime->file_array_size *
+           sizeof(struct hdf5_file_runtime));
+    memset(hdf5_runtime->file_record_array, 0, hdf5_runtime->file_array_size *
+           sizeof(struct darshan_hdf5_file));
+
+    return;
+}
+
+/* get a HDF5 file record for the given file path */
+static struct hdf5_file_runtime* hdf5_file_by_name(const char *name)
+{
+    struct hdf5_file_runtime *file = NULL;
+    char *newname = NULL;
+    darshan_record_id file_id;
+
+    if(!hdf5_runtime || instrumentation_disabled)
+        return(NULL);
+
+    newname = darshan_clean_file_path(name);
+    if(!newname)
+        newname = (char*)name;
+
+    /* get a unique id for this file from darshan core */
+    darshan_core_register_record(
+        (void*)newname,
+        strlen(newname),
+        1,
+        DARSHAN_HDF5_MOD,
+        &file_id,
+        NULL);
+
+    /* search the hash table for this file record, and return if found */
+    HASH_FIND(hlink, hdf5_runtime->file_hash, &file_id, sizeof(darshan_record_id), file);
+    if(file)
+    {
+        if(newname != name)
+            free(newname);
+        return(file);
+    }
+
+    if(hdf5_runtime->file_array_ndx < hdf5_runtime->file_array_size);
+    {
+        /* no existing record, assign a new file record from the global array */
+        file = &(hdf5_runtime->file_runtime_array[hdf5_runtime->file_array_ndx]);
+        file->file_record = &(hdf5_runtime->file_record_array[hdf5_runtime->file_array_ndx]);
+        file->file_record->f_id = file_id;
+        file->file_record->rank = my_rank;
+
+        /* add new record to file hash table */
+        HASH_ADD(hlink, hdf5_runtime->file_hash, file_record->f_id, sizeof(darshan_record_id), file);
+
+        hdf5_runtime->file_array_ndx++;
+    }
+
+    if(newname != name)
+        free(newname);
+    return(file);
+}
+
+/* get a HDF5 file record for the given file path, and also create a
+ * reference structure using the returned hid
+ */
+static struct hdf5_file_runtime* hdf5_file_by_name_sethid(const char* name, hid_t hid)
+{
+    struct hdf5_file_runtime* file;
+    struct hdf5_file_runtime_ref* ref;
+
+    if(!hdf5_runtime || instrumentation_disabled)
+        return(NULL);
+
+    /* find file record by name first */
+    file = hdf5_file_by_name(name);
+
+    if(!file)
+        return(NULL);
+
+    /* search hash table for existing file ref for this fd */
+    HASH_FIND(hlink, hdf5_runtime->hid_hash, &hid, sizeof(hid_t), ref);
+    if(ref)
+    {
+        /* we have a reference.  Make sure it points to the correct file
+         * and return it
+         */
+        ref->file = file;
+        return(file);
+    }
+
+    /* if we hit this point, then we don't have a reference for this fd
+     * in the table yet.  Add it.
+     */
+    ref = malloc(sizeof(*ref));
+    if(!ref)
+        return(NULL);
+    memset(ref, 0, sizeof(*ref));
+
+    ref->file = file;
+    ref->hid = hid;
+    HASH_ADD(hlink, hdf5_runtime->hid_hash, hid, sizeof(hid_t), ref);
+
+    return(file);
+}
+
+/* get a HDF5 file record for the given hid */
+static struct hdf5_file_runtime* hdf5_file_by_hid(hid_t hid)
+{
+    struct hdf5_file_runtime_ref* ref;
+
+    if(!hdf5_runtime || instrumentation_disabled)
+        return(NULL);
+
+    /* search hash table for existing file ref for this hid */
+    HASH_FIND(hlink, hdf5_runtime->hid_hash, &hid, sizeof(hid_t), ref);
+    if(ref)
+        return(ref->file);
+
+    return(NULL);
+}
+
+/* free up HDF5 reference data structures for the given hid */
+static void hdf5_file_close_hid(hid_t hid)
+{
+    struct hdf5_file_runtime_ref* ref;
+
+    if(!hdf5_runtime || instrumentation_disabled)
+        return;
+
+    /* search hash table for this hid */
+    HASH_FIND(hlink, hdf5_runtime->hid_hash, &hid, sizeof(hid_t), ref);
+    if(ref)
+    {
+        /* we have a reference, delete it */
+        HASH_DELETE(hlink, hdf5_runtime->hid_hash, ref);
+        free(ref);
+    }
+
+    return;
+}
+
+/* compare function for sorting file records by descending rank */
+static int hdf5_record_compare(const void* a_p, const void* b_p)
+{
+    const struct darshan_hdf5_file* a = a_p;
+    const struct darshan_hdf5_file* b = b_p;
+
+    if(a->rank < b->rank)
+        return 1;
+    if(a->rank > b->rank)
+        return -1;
+
+    return 0;
+}
+
+static void hdf5_record_reduction_op(void* infile_v, void* inoutfile_v,
+    int *len, MPI_Datatype *datatype)
+{
+    struct darshan_hdf5_file tmp_file;
+    struct darshan_hdf5_file *infile = infile_v;
+    struct darshan_hdf5_file *inoutfile = inoutfile_v;
+    int i, j;
+
+    assert(hdf5_runtime);
+
+    for(i=0; i<*len; i++)
+    {
+        memset(&tmp_file, 0, sizeof(struct darshan_hdf5_file));
+        tmp_file.f_id = infile->f_id;
+        tmp_file.rank = -1;
+
+        /* sum */
+        for(j=HDF5_OPENS; j<=HDF5_OPENS; j++)
+        {
+            tmp_file.counters[j] = infile->counters[j] + inoutfile->counters[j];
+        }
+
+        /* min non-zero (if available) value */
+        for(j=HDF5_F_OPEN_TIMESTAMP; j<=HDF5_F_OPEN_TIMESTAMP; j++)
+        {
+            if(infile->fcounters[j] > inoutfile->fcounters[j] && inoutfile->fcounters[j] > 0)
+                tmp_file.fcounters[j] = inoutfile->fcounters[j];
+            else
+                tmp_file.fcounters[j] = infile->fcounters[j];
+        }
+
+        /* max */
+        for(j=HDF5_F_CLOSE_TIMESTAMP; j<=HDF5_F_CLOSE_TIMESTAMP; j++)
+        {
+            if(infile->fcounters[j] > inoutfile->fcounters[j])
+                tmp_file.fcounters[j] = infile->fcounters[j];
+            else
+                tmp_file.fcounters[j] = inoutfile->fcounters[j];
+        }
+
+        /* update pointers */
+        *inoutfile = tmp_file;
+        inoutfile++;
+        infile++;
+    }
+
+    return;
+}
+
+/************************************************************************
+ * Functions exported by HDF5 module for coordinating with darshan-core *
+ ************************************************************************/
+
+static void hdf5_begin_shutdown()
+{
+    assert(hdf5_runtime);
+
+    HDF5_LOCK();
+    /* disable further instrumentation while Darshan shuts down */
+    instrumentation_disabled = 1;
+    HDF5_UNLOCK();
+
+    return;
+}
+
+static void hdf5_get_output_data(
+    MPI_Comm mod_comm,
+    darshan_record_id *shared_recs,
+    int shared_rec_count,
+    void **hdf5_buf,
+    int *hdf5_buf_sz)
+{
+    struct hdf5_file_runtime *file;
+    int i;
+    struct darshan_hdf5_file *red_send_buf = NULL;
+    struct darshan_hdf5_file *red_recv_buf = NULL;
+    MPI_Datatype red_type;
+    MPI_Op red_op;
+
+    assert(hdf5_runtime);
+
+    /* if there are globally shared files, do a shared file reduction */
+    if(shared_rec_count)
+    {
+        /* necessary initialization of shared records */
+        for(i = 0; i < shared_rec_count; i++)
+        {
+            HASH_FIND(hlink, hdf5_runtime->file_hash, &shared_recs[i],
+                sizeof(darshan_record_id), file);
+            assert(file);
+
+            file->file_record->rank = -1;
+        }
+
+        /* sort the array of files descending by rank so that we get all of the 
+         * shared files (marked by rank -1) in a contiguous portion at end 
+         * of the array
+         */
+        qsort(hdf5_runtime->file_record_array, hdf5_runtime->file_array_ndx,
+            sizeof(struct darshan_hdf5_file), hdf5_record_compare);
+
+        /* make *send_buf point to the shared files at the end of sorted array */
+        red_send_buf =
+            &(hdf5_runtime->file_record_array[hdf5_runtime->file_array_ndx-shared_rec_count]);
+
+        /* allocate memory for the reduction output on rank 0 */
+        if(my_rank == 0)
+        {
+            red_recv_buf = malloc(shared_rec_count * sizeof(struct darshan_hdf5_file));
+            if(!red_recv_buf)
+                return;
+        }
+
+        /* construct a datatype for a HDF5 file record.  This is serving no purpose
+         * except to make sure we can do a reduction on proper boundaries
+         */
+        DARSHAN_MPI_CALL(PMPI_Type_contiguous)(sizeof(struct darshan_hdf5_file),
+            MPI_BYTE, &red_type);
+        DARSHAN_MPI_CALL(PMPI_Type_commit)(&red_type);
+
+        /* register a HDF5 file record reduction operator */
+        DARSHAN_MPI_CALL(PMPI_Op_create)(hdf5_record_reduction_op, 1, &red_op);
+
+        /* reduce shared HDF5 file records */
+        DARSHAN_MPI_CALL(PMPI_Reduce)(red_send_buf, red_recv_buf,
+            shared_rec_count, red_type, red_op, 0, mod_comm);
+
+        /* clean up reduction state */
+        if(my_rank == 0)
+        {
+            int tmp_ndx = hdf5_runtime->file_array_ndx - shared_rec_count;
+            memcpy(&(hdf5_runtime->file_record_array[tmp_ndx]), red_recv_buf,
+                shared_rec_count * sizeof(struct darshan_hdf5_file));
+            free(red_recv_buf);
+        }
+        else
+        {
+            hdf5_runtime->file_array_ndx -= shared_rec_count;
+        }
+
+        DARSHAN_MPI_CALL(PMPI_Type_free)(&red_type);
+        DARSHAN_MPI_CALL(PMPI_Op_free)(&red_op);
+    }
+
+    *hdf5_buf = (void *)(hdf5_runtime->file_record_array);
+    *hdf5_buf_sz = hdf5_runtime->file_array_ndx * sizeof(struct darshan_hdf5_file);
+
+    return;
+}
+
+static void hdf5_shutdown()
+{
+    struct hdf5_file_runtime_ref *ref, *tmp;
+
+    assert(hdf5_runtime);
+
+    HASH_ITER(hlink, hdf5_runtime->hid_hash, ref, tmp)
+    {
+        HASH_DELETE(hlink, hdf5_runtime->hid_hash, ref);
+        free(ref);
+    }
+
+    HASH_CLEAR(hlink, hdf5_runtime->file_hash); /* these entries are freed all at once below */
+
+    free(hdf5_runtime->file_runtime_array);
+    free(hdf5_runtime->file_record_array);
+    free(hdf5_runtime);
+    hdf5_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-mpiio.c b/darshan-runtime/lib/darshan-mpiio.c
index 7971191..c823851 100644
--- a/darshan-runtime/lib/darshan-mpiio.c
+++ b/darshan-runtime/lib/darshan-mpiio.c
@@ -218,7 +218,6 @@ int MPI_File_open(MPI_Comm comm, char *filename, int amode, MPI_Info info, MPI_F
         file = mpiio_file_by_name_setfh(filename, (*fh));
         if(file)
         {
-            file->file_record->rank = my_rank;
             file->file_record->counters[MPIIO_MODE] = amode;
             DARSHAN_MPI_CALL(PMPI_Comm_size)(comm, &comm_size);
             if(comm_size == 1)
@@ -911,6 +910,7 @@ static struct mpiio_file_runtime* mpiio_file_by_name(const char *name)
         file = &(mpiio_runtime->file_runtime_array[mpiio_runtime->file_array_ndx]);
         file->file_record = &(mpiio_runtime->file_record_array[mpiio_runtime->file_array_ndx]);
         file->file_record->f_id = file_id;
+        file->file_record->rank = my_rank;
 
         /* add new record to file hash table */
         HASH_ADD(hlink, mpiio_runtime->file_hash, file_record->f_id, sizeof(darshan_record_id), file);
@@ -1369,7 +1369,7 @@ static void mpiio_get_output_data(
 
         /* make *send_buf point to the shared files at the end of sorted array */
         red_send_buf =
-            &(mpiio_runtime->file_record_array[mpiio_runtime->file_array_ndx-(shared_rec_count)]);
+            &(mpiio_runtime->file_record_array[mpiio_runtime->file_array_ndx-shared_rec_count]);
 
         /* allocate memory for the reduction output on rank 0 */
         if(my_rank == 0)
diff --git a/darshan-runtime/lib/darshan-pnetcdf.c b/darshan-runtime/lib/darshan-pnetcdf.c
new file mode 100644
index 0000000..5f4c0d7
--- /dev/null
+++ b/darshan-runtime/lib/darshan-pnetcdf.c
@@ -0,0 +1,588 @@
+/*
+ * Copyright (C) 2015 University of Chicago.
+ * See COPYRIGHT notice in top-level directory.
+ *
+ */
+
+#include "darshan-runtime-config.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdarg.h>
+#include <string.h>
+#include <time.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <search.h>
+#include <assert.h>
+#define __USE_GNU
+#include <pthread.h>
+
+#include "uthash.h"
+
+#include "darshan.h"
+#include "darshan-pnetcdf-log-format.h"
+#include "darshan-dynamic.h"
+
+DARSHAN_FORWARD_DECL(ncmpi_create, int, (MPI_Comm comm, const char *path, int cmode, MPI_Info info, int *ncidp));
+DARSHAN_FORWARD_DECL(ncmpi_open, int, (MPI_Comm comm, const char *path, int omode, MPI_Info info, int *ncidp));
+DARSHAN_FORWARD_DECL(ncmpi_close, int, (int ncid));
+
+/* structure to track i/o stats for a given PNETCDF file at runtime */
+struct pnetcdf_file_runtime
+{
+    struct darshan_pnetcdf_file* file_record;
+    UT_hash_handle hlink;
+};
+
+/* structure to associate a PNETCDF ncid with an existing file runtime structure */
+struct pnetcdf_file_runtime_ref
+{
+    struct pnetcdf_file_runtime* file;
+    int ncid;
+    UT_hash_handle hlink;
+};
+
+/* necessary state for storing PNETCDF file records and coordinating with
+ * darshan-core at shutdown time
+ */
+struct pnetcdf_runtime
+{
+    struct pnetcdf_file_runtime* file_runtime_array;
+    struct darshan_pnetcdf_file* file_record_array;
+    int file_array_size;
+    int file_array_ndx;
+    struct pnetcdf_file_runtime *file_hash;
+    struct pnetcdf_file_runtime_ref* ncid_hash;
+};
+
+static struct pnetcdf_runtime *pnetcdf_runtime = NULL;
+static pthread_mutex_t pnetcdf_runtime_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+static int instrumentation_disabled = 0;
+static int my_rank = -1;
+
+static void pnetcdf_runtime_initialize(void);
+static struct pnetcdf_file_runtime* pnetcdf_file_by_name(const char *name);
+static struct pnetcdf_file_runtime* pnetcdf_file_by_name_setncid(const char* name, int ncid);
+static struct pnetcdf_file_runtime* pnetcdf_file_by_ncid(int ncid);
+static void pnetcdf_file_close_ncid(int ncid);
+static int pnetcdf_record_compare(const void* a, const void* b);
+static void pnetcdf_record_reduction_op(void* infile_v, void* inoutfile_v,
+    int *len, MPI_Datatype *datatype);
+
+static void pnetcdf_begin_shutdown(void);
+static void pnetcdf_get_output_data(MPI_Comm mod_comm, darshan_record_id *shared_recs,
+    int shared_rec_count, void **pnetcdf_buf, int *pnetcdf_buf_sz);
+static void pnetcdf_shutdown(void);
+
+#define PNETCDF_LOCK() pthread_mutex_lock(&pnetcdf_runtime_mutex)
+#define PNETCDF_UNLOCK() pthread_mutex_unlock(&pnetcdf_runtime_mutex)
+
+/*********************************************************
+ *      Wrappers for PNETCDF functions of interest       * 
+ *********************************************************/
+
+int DARSHAN_DECL(ncmpi_create)(MPI_Comm comm, const char *path,
+    int cmode, MPI_Info info, int *ncidp)
+{
+    int ret;
+    struct pnetcdf_file_runtime* file;
+    char* tmp;
+    int comm_size;
+    double tm1;
+
+    MAP_OR_FAIL(ncmpi_create);
+
+    tm1 = darshan_core_wtime();
+    ret = __real_ncmpi_create(comm, path, cmode, info, ncidp);
+    if(ret == 0)
+    {
+        /* use ROMIO approach to strip prefix if present */
+        /* strip off prefix if there is one, but only skip prefixes
+         * if they are greater than length one to allow for windows
+         * drive specifications (e.g. c:\...) 
+         */
+        tmp = strchr(path, ':');
+        if (tmp > path + 1) {
+            path = tmp + 1;
+        }
+
+        PNETCDF_LOCK();
+        pnetcdf_runtime_initialize();
+        file = pnetcdf_file_by_name_setncid(path, (*ncidp));
+        if(file)
+        {
+            if(file->file_record->fcounters[PNETCDF_F_OPEN_TIMESTAMP] == 0)
+                file->file_record->fcounters[PNETCDF_F_OPEN_TIMESTAMP] = tm1;
+            DARSHAN_MPI_CALL(PMPI_Comm_size)(comm, &comm_size);
+            if(comm_size == 1)
+            {
+                file->file_record->counters[PNETCDF_INDEP_OPENS] += 1;
+            }
+            else
+            {
+                file->file_record->counters[PNETCDF_COLL_OPENS] += 1;
+            }
+        }
+        PNETCDF_UNLOCK();
+    }
+
+    return(ret);
+}
+
+int DARSHAN_DECL(ncmpi_open)(MPI_Comm comm, const char *path,
+    int omode, MPI_Info info, int *ncidp)
+{
+    int ret;
+    struct pnetcdf_file_runtime* file;
+    char* tmp;
+    int comm_size;
+    double tm1;
+
+    MAP_OR_FAIL(ncmpi_open);
+
+    tm1 = darshan_core_wtime();
+    ret = __real_ncmpi_open(comm, path, omode, info, ncidp);
+    if(ret == 0)
+    {
+        /* use ROMIO approach to strip prefix if present */
+        /* strip off prefix if there is one, but only skip prefixes
+         * if they are greater than length one to allow for windows
+         * drive specifications (e.g. c:\...) 
+         */
+        tmp = strchr(path, ':');
+        if (tmp > path + 1) {
+            path = tmp + 1;
+        }
+
+        PNETCDF_LOCK();
+        pnetcdf_runtime_initialize();
+        file = pnetcdf_file_by_name_setncid(path, (*ncidp));
+        if(file)
+        {
+            if(file->file_record->fcounters[PNETCDF_F_OPEN_TIMESTAMP] == 0)
+                file->file_record->fcounters[PNETCDF_F_OPEN_TIMESTAMP] = tm1;
+            DARSHAN_MPI_CALL(PMPI_Comm_size)(comm, &comm_size);
+            if(comm_size == 1)
+            {
+                file->file_record->counters[PNETCDF_INDEP_OPENS] += 1;
+            }
+            else
+            {
+                file->file_record->counters[PNETCDF_COLL_OPENS] += 1;
+            }
+        }
+        PNETCDF_UNLOCK();
+    }
+
+    return(ret);
+}
+
+int DARSHAN_DECL(ncmpi_close)(int ncid)
+{
+    struct pnetcdf_file_runtime* file;
+    int ret;
+
+    MAP_OR_FAIL(ncmpi_close);
+
+    ret = __real_ncmpi_close(ncid);
+
+    PNETCDF_LOCK();
+    pnetcdf_runtime_initialize();
+    file = pnetcdf_file_by_ncid(ncid);
+    if(file)
+    {
+        file->file_record->fcounters[PNETCDF_F_CLOSE_TIMESTAMP] =
+            darshan_core_wtime();
+        pnetcdf_file_close_ncid(ncid);
+    }
+    PNETCDF_UNLOCK();
+
+    return(ret);
+}
+
+/************************************************************
+ * Internal functions for manipulating PNETCDF module state *
+ ************************************************************/
+
+/* initialize internal PNETCDF module data strucutres and register with darshan-core */
+static void pnetcdf_runtime_initialize()
+{
+    int mem_limit;
+    struct darshan_module_funcs pnetcdf_mod_fns =
+    {
+        .begin_shutdown = &pnetcdf_begin_shutdown,
+        .get_output_data = &pnetcdf_get_output_data,
+        .shutdown = &pnetcdf_shutdown
+    };
+
+    /* don't do anything if already initialized or instrumenation is disabled */
+    if(pnetcdf_runtime || instrumentation_disabled)
+        return;
+
+    /* register pnetcdf module with darshan-core */
+    darshan_core_register_module(
+        DARSHAN_PNETCDF_MOD,
+        &pnetcdf_mod_fns,
+        &my_rank,
+        &mem_limit,
+        NULL);
+
+    /* return if no memory assigned by darshan-core */
+    if(mem_limit == 0)
+        return;
+
+    pnetcdf_runtime = malloc(sizeof(*pnetcdf_runtime));
+    if(!pnetcdf_runtime)
+        return;
+    memset(pnetcdf_runtime, 0, sizeof(*pnetcdf_runtime));
+
+    /* set maximum number of file records according to max memory limit */
+    /* NOTE: maximum number of records is based on the size of a pnetcdf file record */
+    /* TODO: should we base memory usage off file record or total runtime structure sizes? */
+    pnetcdf_runtime->file_array_size = mem_limit / sizeof(struct darshan_pnetcdf_file);
+    pnetcdf_runtime->file_array_ndx = 0;
+
+    /* allocate array of runtime file records */
+    pnetcdf_runtime->file_runtime_array = malloc(pnetcdf_runtime->file_array_size *
+                                                 sizeof(struct pnetcdf_file_runtime));
+    pnetcdf_runtime->file_record_array = malloc(pnetcdf_runtime->file_array_size *
+                                                sizeof(struct darshan_pnetcdf_file));
+    if(!pnetcdf_runtime->file_runtime_array || !pnetcdf_runtime->file_record_array)
+    {
+        pnetcdf_runtime->file_array_size = 0;
+        return;
+    }
+    memset(pnetcdf_runtime->file_runtime_array, 0, pnetcdf_runtime->file_array_size *
+           sizeof(struct pnetcdf_file_runtime));
+    memset(pnetcdf_runtime->file_record_array, 0, pnetcdf_runtime->file_array_size *
+           sizeof(struct darshan_pnetcdf_file));
+
+    return;
+}
+
+/* get a PNETCDF file record for the given file path */
+static struct pnetcdf_file_runtime* pnetcdf_file_by_name(const char *name)
+{
+    struct pnetcdf_file_runtime *file = NULL;
+    char *newname = NULL;
+    darshan_record_id file_id;
+
+    if(!pnetcdf_runtime || instrumentation_disabled)
+        return(NULL);
+
+    newname = darshan_clean_file_path(name);
+    if(!newname)
+        newname = (char*)name;
+
+    /* get a unique id for this file from darshan core */
+    darshan_core_register_record(
+        (void*)newname,
+        strlen(newname),
+        1,
+        DARSHAN_PNETCDF_MOD,
+        &file_id,
+        NULL);
+
+    /* search the hash table for this file record, and return if found */
+    HASH_FIND(hlink, pnetcdf_runtime->file_hash, &file_id, sizeof(darshan_record_id), file);
+    if(file)
+    {
+        if(newname != name)
+            free(newname);
+        return(file);
+    }
+
+    if(pnetcdf_runtime->file_array_ndx < pnetcdf_runtime->file_array_size);
+    {
+        /* no existing record, assign a new file record from the global array */
+        file = &(pnetcdf_runtime->file_runtime_array[pnetcdf_runtime->file_array_ndx]);
+        file->file_record = &(pnetcdf_runtime->file_record_array[pnetcdf_runtime->file_array_ndx]);
+        file->file_record->f_id = file_id;
+        file->file_record->rank = my_rank;
+
+        /* add new record to file hash table */
+        HASH_ADD(hlink, pnetcdf_runtime->file_hash, file_record->f_id, sizeof(darshan_record_id), file);
+
+        pnetcdf_runtime->file_array_ndx++;
+    }
+
+    if(newname != name)
+        free(newname);
+    return(file);
+}
+
+/* get a PNETCDF file record for the given file path, and also create a
+ * reference structure using the returned ncid
+ */
+static struct pnetcdf_file_runtime* pnetcdf_file_by_name_setncid(const char* name, int ncid)
+{
+    struct pnetcdf_file_runtime* file;
+    struct pnetcdf_file_runtime_ref* ref;
+
+    if(!pnetcdf_runtime || instrumentation_disabled)
+        return(NULL);
+
+    /* find file record by name first */
+    file = pnetcdf_file_by_name(name);
+
+    if(!file)
+        return(NULL);
+
+    /* search hash table for existing file ref for this ncid */
+    HASH_FIND(hlink, pnetcdf_runtime->ncid_hash, &ncid, sizeof(int), ref);
+    if(ref)
+    {
+        /* we have a reference.  Make sure it points to the correct file
+         * and return it
+         */
+        ref->file = file;
+        return(file);
+    }
+
+    /* if we hit this point, then we don't have a reference for this ncid
+     * in the table yet.  Add it.
+     */
+    ref = malloc(sizeof(*ref));
+    if(!ref)
+        return(NULL);
+    memset(ref, 0, sizeof(*ref));
+
+    ref->file = file;
+    ref->ncid = ncid;
+    HASH_ADD(hlink, pnetcdf_runtime->ncid_hash, ncid, sizeof(int), ref);
+
+    return(file);
+}
+
+/* get a PNETCDF file record for the given ncid */
+static struct pnetcdf_file_runtime* pnetcdf_file_by_ncid(int ncid)
+{
+    struct pnetcdf_file_runtime_ref* ref;
+
+    if(!pnetcdf_runtime || instrumentation_disabled)
+        return(NULL);
+
+    /* search hash table for existing file ref for this ncid */
+    HASH_FIND(hlink, pnetcdf_runtime->ncid_hash, &ncid, sizeof(int), ref);
+    if(ref)
+        return(ref->file);
+
+    return(NULL);
+}
+
+/* free up PNETCDF reference data structures for the given ncid */
+static void pnetcdf_file_close_ncid(int ncid)
+{
+    struct pnetcdf_file_runtime_ref* ref;
+
+    if(!pnetcdf_runtime || instrumentation_disabled)
+        return;
+
+    /* search hash table for this ncid */
+    HASH_FIND(hlink, pnetcdf_runtime->ncid_hash, &ncid, sizeof(int), ref);
+    if(ref)
+    {
+        /* we have a reference, delete it */
+        HASH_DELETE(hlink, pnetcdf_runtime->ncid_hash, ref);
+        free(ref);
+    }
+
+    return;
+}
+
+/* compare function for sorting file records by descending rank */
+static int pnetcdf_record_compare(const void* a_p, const void* b_p)
+{
+    const struct darshan_pnetcdf_file* a = a_p;
+    const struct darshan_pnetcdf_file* b = b_p;
+
+    if(a->rank < b->rank)
+        return 1;
+    if(a->rank > b->rank)
+        return -1;
+
+    return 0;
+}
+
+static void pnetcdf_record_reduction_op(void* infile_v, void* inoutfile_v,
+    int *len, MPI_Datatype *datatype)
+{
+    struct darshan_pnetcdf_file tmp_file;
+    struct darshan_pnetcdf_file *infile = infile_v;
+    struct darshan_pnetcdf_file *inoutfile = inoutfile_v;
+    int i, j;
+
+    assert(pnetcdf_runtime);
+
+    for(i=0; i<*len; i++)
+    {
+        memset(&tmp_file, 0, sizeof(struct darshan_pnetcdf_file));
+        tmp_file.f_id = infile->f_id;
+        tmp_file.rank = -1;
+
+        /* sum */
+        for(j=PNETCDF_INDEP_OPENS; j<=PNETCDF_COLL_OPENS; j++)
+        {
+            tmp_file.counters[j] = infile->counters[j] + inoutfile->counters[j];
+        }
+
+        /* min non-zero (if available) value */
+        for(j=PNETCDF_F_OPEN_TIMESTAMP; j<=PNETCDF_F_OPEN_TIMESTAMP; j++)
+        {
+            if(infile->fcounters[j] > inoutfile->fcounters[j] && inoutfile->fcounters[j] > 0)
+                tmp_file.fcounters[j] = inoutfile->fcounters[j];
+            else
+                tmp_file.fcounters[j] = infile->fcounters[j];
+        }
+
+        /* max */
+        for(j=PNETCDF_F_CLOSE_TIMESTAMP; j<=PNETCDF_F_CLOSE_TIMESTAMP; j++)
+        {
+            if(infile->fcounters[j] > inoutfile->fcounters[j])
+                tmp_file.fcounters[j] = infile->fcounters[j];
+            else
+                tmp_file.fcounters[j] = inoutfile->fcounters[j];
+        }
+
+        /* update pointers */
+        *inoutfile = tmp_file;
+        inoutfile++;
+        infile++;
+    }
+
+    return;
+}
+
+/***************************************************************************
+ * Functions exported by PNETCDF module for coordinating with darshan-core *
+ ***************************************************************************/
+
+static void pnetcdf_begin_shutdown()
+{
+    assert(pnetcdf_runtime);
+
+    PNETCDF_LOCK();
+    /* disable further instrumentation while Darshan shuts down */
+    instrumentation_disabled = 1;
+    PNETCDF_UNLOCK();
+
+    return;
+}
+
+static void pnetcdf_get_output_data(
+    MPI_Comm mod_comm,
+    darshan_record_id *shared_recs,
+    int shared_rec_count,
+    void **pnetcdf_buf,
+    int *pnetcdf_buf_sz)
+{
+    struct pnetcdf_file_runtime *file;
+    int i;
+    struct darshan_pnetcdf_file *red_send_buf = NULL;
+    struct darshan_pnetcdf_file *red_recv_buf = NULL;
+    MPI_Datatype red_type;
+    MPI_Op red_op;
+
+    assert(pnetcdf_runtime);
+
+    /* if there are globally shared files, do a shared file reduction */
+    if(shared_rec_count)
+    {
+        /* necessary initialization of shared records */
+        for(i = 0; i < shared_rec_count; i++)
+        {
+            HASH_FIND(hlink, pnetcdf_runtime->file_hash, &shared_recs[i],
+                sizeof(darshan_record_id), file);
+            assert(file);
+
+            file->file_record->rank = -1;
+        }
+
+        /* sort the array of files descending by rank so that we get all of the 
+         * shared files (marked by rank -1) in a contiguous portion at end 
+         * of the array
+         */
+        qsort(pnetcdf_runtime->file_record_array, pnetcdf_runtime->file_array_ndx,
+            sizeof(struct darshan_pnetcdf_file), pnetcdf_record_compare);
+
+        /* make *send_buf point to the shared files at the end of sorted array */
+        red_send_buf =
+            &(pnetcdf_runtime->file_record_array[pnetcdf_runtime->file_array_ndx-shared_rec_count]);
+
+        /* allocate memory for the reduction output on rank 0 */
+        if(my_rank == 0)
+        {
+            red_recv_buf = malloc(shared_rec_count * sizeof(struct darshan_pnetcdf_file));
+            if(!red_recv_buf)
+                return;
+        }
+
+        /* construct a datatype for a PNETCDF file record.  This is serving no purpose
+         * except to make sure we can do a reduction on proper boundaries
+         */
+        DARSHAN_MPI_CALL(PMPI_Type_contiguous)(sizeof(struct darshan_pnetcdf_file),
+            MPI_BYTE, &red_type);
+        DARSHAN_MPI_CALL(PMPI_Type_commit)(&red_type);
+
+        /* register a PNETCDF file record reduction operator */
+        DARSHAN_MPI_CALL(PMPI_Op_create)(pnetcdf_record_reduction_op, 1, &red_op);
+
+        /* reduce shared PNETCDF file records */
+        DARSHAN_MPI_CALL(PMPI_Reduce)(red_send_buf, red_recv_buf,
+            shared_rec_count, red_type, red_op, 0, mod_comm);
+
+        /* clean up reduction state */
+        if(my_rank == 0)
+        {
+            int tmp_ndx = pnetcdf_runtime->file_array_ndx - shared_rec_count;
+            memcpy(&(pnetcdf_runtime->file_record_array[tmp_ndx]), red_recv_buf,
+                shared_rec_count * sizeof(struct darshan_pnetcdf_file));
+            free(red_recv_buf);
+        }
+        else
+        {
+            pnetcdf_runtime->file_array_ndx -= shared_rec_count;
+        }
+
+        DARSHAN_MPI_CALL(PMPI_Type_free)(&red_type);
+        DARSHAN_MPI_CALL(PMPI_Op_free)(&red_op);
+    }
+
+    *pnetcdf_buf = (void *)(pnetcdf_runtime->file_record_array);
+    *pnetcdf_buf_sz = pnetcdf_runtime->file_array_ndx * sizeof(struct darshan_pnetcdf_file);
+
+    return;
+}
+
+static void pnetcdf_shutdown()
+{
+    struct pnetcdf_file_runtime_ref *ref, *tmp;
+
+    assert(pnetcdf_runtime);
+
+    HASH_ITER(hlink, pnetcdf_runtime->ncid_hash, ref, tmp)
+    {
+        HASH_DELETE(hlink, pnetcdf_runtime->ncid_hash, ref);
+        free(ref);
+    }
+
+    HASH_CLEAR(hlink, pnetcdf_runtime->file_hash); /* these entries are freed all at once below */
+
+    free(pnetcdf_runtime->file_runtime_array);
+    free(pnetcdf_runtime->file_record_array);
+    free(pnetcdf_runtime);
+    pnetcdf_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 1d0d7af..c6c1ec7 100644
--- a/darshan-runtime/lib/darshan-posix.c
+++ b/darshan-runtime/lib/darshan-posix.c
@@ -220,7 +220,6 @@ static void posix_shutdown(void);
     if(exclude) break; \
     file = posix_file_by_name_setfd(__path, __ret); \
     if(!file) break; \
-    file->file_record->rank = my_rank; \
     if(__mode) \
         file->file_record->counters[POSIX_MODE] = __mode; \
     file->offset = 0; \
@@ -355,7 +354,6 @@ static void posix_shutdown(void);
 } while(0)
 
 #define POSIX_RECORD_STAT(__file, __statbuf, __tm1, __tm2) do { \
-    (__file)->file_record->rank = my_rank; \
     DARSHAN_TIMER_INC_NO_OVERLAP((__file)->file_record->fcounters[POSIX_F_META_TIME], __tm1, __tm2, (__file)->last_meta_end); \
     (__file)->file_record->counters[POSIX_STATS] += 1; \
 } while(0)
@@ -1543,6 +1541,7 @@ static struct posix_file_runtime* posix_file_by_name(const char *name)
         file = &(posix_runtime->file_runtime_array[posix_runtime->file_array_ndx]);
         file->file_record = &(posix_runtime->file_record_array[posix_runtime->file_array_ndx]);
         file->file_record->f_id = file_id;
+        file->file_record->rank = my_rank;
         file->file_record->counters[POSIX_MEM_ALIGNMENT] = darshan_mem_alignment;
         file->file_record->counters[POSIX_FILE_ALIGNMENT] = file_alignment;
 
@@ -1711,7 +1710,6 @@ static void posix_record_reduction_op(void* infile_v, void* inoutfile_v,
     for(i=0; i<*len; i++)
     {
         memset(&tmp_file, 0, sizeof(struct darshan_posix_file));
-
         tmp_file.f_id = infile->f_id;
         tmp_file.rank = -1;
 
@@ -1823,7 +1821,8 @@ static void posix_record_reduction_op(void* infile_v, void* inoutfile_v,
         /* min non-zero (if available) value */
         for(j=POSIX_F_OPEN_TIMESTAMP; j<=POSIX_F_WRITE_START_TIMESTAMP; j++)
         {
-            if(infile->fcounters[j] > inoutfile->fcounters[j] && inoutfile->fcounters[j] > 0)
+            if(infile->fcounters[j] > inoutfile->fcounters[j] &&
+               inoutfile->fcounters[j] > 0)
                 tmp_file.fcounters[j] = inoutfile->fcounters[j];
             else
                 tmp_file.fcounters[j] = infile->fcounters[j];
@@ -2108,7 +2107,7 @@ static void posix_get_output_data(
 
         /* make *send_buf point to the shared files at the end of sorted array */
         red_send_buf =
-            &(posix_runtime->file_record_array[posix_runtime->file_array_ndx-(shared_rec_count)]);
+            &(posix_runtime->file_record_array[posix_runtime->file_array_ndx-shared_rec_count]);
         
         /* allocate memory for the reduction output on rank 0 */
         if(my_rank == 0)
diff --git a/darshan-util/Makefile.in b/darshan-util/Makefile.in
index d3c9f6a..6b37efa 100644
--- a/darshan-util/Makefile.in
+++ b/darshan-util/Makefile.in
@@ -1,4 +1,4 @@
-all: libdarshan-util.a darshan-base-parser darshan-posix-parser darshan-mpiio-parser
+all: libdarshan-util.a darshan-parser
 
 DESTDIR =
 srcdir = @srcdir@
@@ -65,18 +65,9 @@ libdarshan-util.a: darshan-logutils.o darshan-posix-logutils.o darshan-mpiio-log
 jenkins: util/bin/jenkins.o lookup3.o
 	$(CC) $(CFLAGS)  $(LDFLAGS) $< -o $@ lookup3.o $(LIBS)
 
-darshan-base-parser: darshan-base-parser.c darshan-logutils.h $(DARSHAN_LOG_FORMAT) libdarshan-util.a | uthash-1.9.2
+darshan-parser: darshan-parser.c darshan-logutils.h darshan-posix-logutils.h darshan-mpiio-logutils.h $(DARSHAN_LOG_FORMAT) $(srcdir)/../darshan-posix-log-format.h $(srcdir)/../darshan-mpiio-log-format.h libdarshan-util.a | uthash-1.9.2
 	$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) 
 
-darshan-posix-parser: darshan-posix-parser.c darshan-logutils.h darshan-posix-logutils.h $(DARSHAN_LOG_FORMAT) $(srcdir)/../darshan-posix-log-format.h libdarshan-util.a | uthash-1.9.2
-	$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) 
-
-darshan-mpiio-parser: darshan-mpiio-parser.c darshan-logutils.h darshan-mpiio-logutils.h $(DARSHAN_LOG_FORMAT) $(srcdir)/../darshan-mpiio-log-format.h libdarshan-util.a | uthash-1.9.2
-	$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) 
-
-#darshan-parser: darshan-parser.c $(DARSHAN_LOG_FORMAT) darshan-logutils.h darshan-logutils.o | uthash-1.9.2
-#	$(CC) $(CFLAGS) $(LDFLAGS) $< darshan-logutils.o -o $@ $(LIBS) 
-
 #darshan-convert: darshan-convert.c $(DARSHAN_LOG_FORMAT) darshan-logutils.h darshan-logutils.o lookup3.o
 #	$(CC) $(CFLAGS)  $(LDFLAGS) $< darshan-logutils.o lookup3.o -o $@ $(LIBS)
 
@@ -105,10 +96,7 @@ install:: all
 	install -d $(libdir)
 	install -d $(includedir)
 	install -d $(pkgconfigdir)
-#	install -m 755 darshan-parser $(bindir)
-	install -m 755 darshan-base-parser $(bindir)
-	install -m 755 darshan-posix-parser $(bindir)
-	install -m 755 darshan-mpiio-parser $(bindir)
+	install -m 755 darshan-parser $(bindir)
 #	install -m 755 darshan-convert $(bindir)
 #	install -m 755 darshan-diff $(bindir)
 #	install -m 755 darshan-analyzer $(bindir)
@@ -131,7 +119,7 @@ endif
 
 
 clean::
-	rm -f *.o *.a darshan-base-parser darshan-posix-parser darshan-mpiio-parser
+	rm -f *.o *.a darshan-parser darshan-posix-parser darshan-mpiio-parser
 
 distclean:: clean
 	rm -f darshan-runtime-config.h aclocal.m4 autom4te.cache/* config.status config.log Makefile util/bin/darshan-job-summary.pl
diff --git a/darshan-util/darshan-logutils.c b/darshan-util/darshan-logutils.c
index a94eda3..036a5a5 100644
--- a/darshan-util/darshan-logutils.c
+++ b/darshan-util/darshan-logutils.c
@@ -21,7 +21,28 @@
 
 static int darshan_log_seek(darshan_fd fd, off_t offset);
 static int darshan_log_read(darshan_fd fd, void *buf, int len);
-static int darshan_log_write(darshan_fd fd, void *buf, int len);
+//static int darshan_log_write(darshan_fd fd, void *buf, int len);
+
+/* TODO: can we make this s.t. we don't care about ordering (i.e., X macro it ) */
+struct darshan_mod_logutil_funcs *mod_logutils[DARSHAN_MAX_MODS] =
+{
+    NULL,   /* NULL */
+    &posix_logutils,    /* POSIX */
+    &mpiio_logutils,   /* MPI-IO */
+    NULL,   /* HDF5 */
+    NULL,   /* PNETCDF */
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL
+};
 
 /* darshan_log_open()
  *
@@ -42,16 +63,6 @@ darshan_fd darshan_log_open(const char *name, const char *mode)
         return(NULL);
     memset(tmp_fd, 0, sizeof(*tmp_fd));
 
-    if(strcmp(mode, "r") == 0)
-    {
-        /* TODO: check for bz2 */
-    }
-    
-    if(strcmp(mode, "w") == 0)
-    {
-        /* TODO: check for bz2 */
-    }
-
     tmp_fd->gzf = gzopen(name, mode);
     if(!tmp_fd->gzf)
     {
@@ -406,8 +417,6 @@ void darshan_log_close(darshan_fd fd)
     if(fd->gzf)
         gzclose(fd->gzf);
 
-    /* TODO: check bz2 */
-
     free(fd);
 
     return;
@@ -437,11 +446,10 @@ static int darshan_log_seek(darshan_fd fd, off_t offset)
         return(-1);
     }
 
-    /* TODO: check bz2 */
-
     return(-1);
 }
 
+#if 0
 /* return amount written on success, -1 on failure.
  */
 static int darshan_log_write(darshan_fd fd, void* buf, int len)
@@ -456,10 +464,9 @@ static int darshan_log_write(darshan_fd fd, void* buf, int len)
         return(ret);
     }
 
-    /* TODO: check bz2 */
-
     return(-1);
 }
+#endif
 
 /* return amount read on success, 0 on EOF, -1 on failure.
  */
@@ -475,8 +482,6 @@ static int darshan_log_read(darshan_fd fd, void* buf, int len)
         return(ret);
     }
 
-    /* TODO: check bz2 */
-
     return(-1);
 }
 
diff --git a/darshan-util/darshan-logutils.h b/darshan-util/darshan-logutils.h
index b6bb37a..709ac60 100644
--- a/darshan-util/darshan-logutils.h
+++ b/darshan-util/darshan-logutils.h
@@ -12,14 +12,13 @@
 #include <bzlib.h>
 #endif
 
-#include "darshan-log-format.h"
 #include "uthash-1.9.2/src/uthash.h"
 
-/* default to a compression buffer size of 4 MiB */
+#include "darshan-log-format.h"
+
+/* default to a decompression buffer size of 4 MiB */
 #define DARSHAN_DEF_DECOMP_BUF_SZ (4*1024*1024)
 
-/* TODO: we need to refactor s.t. utilities don't know implementation
-   of this, but module-specific functions do */
 struct darshan_fd_s
 {
     gzFile gzf;
@@ -37,6 +36,26 @@ struct darshan_record_ref
     UT_hash_handle hlink;
 };
 
+struct darshan_mod_logutil_funcs
+{
+    int (*log_get_record)(
+        darshan_fd fd,
+        void **rec_dat,
+        darshan_record_id *rec_id
+    );
+    void (*log_print_record)(
+        void *file_rec,
+        char *file_name,
+        char *mnt_pt,
+        char *fs_type
+    );
+};
+
+extern struct darshan_mod_logutil_funcs *mod_logutils[DARSHAN_MAX_MODS];
+
+#include "darshan-posix-logutils.h"
+#include "darshan-mpiio-logutils.h"
+
 darshan_fd darshan_log_open(const char *name, const char* mode);
 int darshan_log_getheader(darshan_fd fd, struct darshan_header *header);
 int darshan_log_getjob(darshan_fd fd, struct darshan_job *job);
@@ -48,19 +67,25 @@ int darshan_log_get_moddat(darshan_fd fd, darshan_module_id mod_id,
     void *moddat_buf, int moddat_buf_sz);
 void darshan_log_close(darshan_fd file);
 
-/* convenience macros for printing out counters */
-#define CP_PRINT_HEADER() printf("#<rank>\t<file>\t<counter>\t<value>\t<name suffix>\t<mount pt>\t<fs type>\n")
-#define CP_PRINT(__job, __file, __counter, __mnt_pt, __fs_type) do {\
-        printf("%" PRId64 "\t%" PRIu64 "\t%s\t%" PRId64 "\t...%s\t%s\t%s\n", \
-            (__file)->rank, (__file)->hash, darshan_names[__counter], \
-            (__file)->counters[__counter], (__file)->name_suffix, \
-            __mnt_pt, __fs_type); \
+/* convenience macros for printing Darshan counters */
+#define DARSHAN_PRINT_HEADER() \
+    printf("\n#<module>\t<rank>\t<file>\t<counter>\t<value>" \
+           "\t<file name>\t<mount pt>\t<fs type>\n")
+
+#define DARSHAN_COUNTER_PRINT(__mod_name, __rank, __file_id, \
+                              __counter, __counter_val, __file_name, \
+                              __mnt_pt, __fs_type) do { \
+    printf("%s\t%" PRId64 "\t%" PRIu64 "\t%s\t%" PRId64 "\t%s\t%s\t%s\n", \
+        __mod_name, __rank, __file_id, __counter, __counter_val, \
+        __file_name, __mnt_pt, __fs_type); \
 } while(0)
-#define CP_F_PRINT(__job, __file, __counter, __mnt_pt, __fs_type) do {\
-        printf("%" PRId64 "\t%" PRIu64 "\t%s\t%f\t...%s\t%s\t%s\n", \
-            (__file)->rank, (__file)->hash, darshan_f_names[__counter], \
-            (__file)->fcounters[__counter], (__file)->name_suffix, \
-            __mnt_pt, __fs_type); \
+
+#define DARSHAN_F_COUNTER_PRINT(__mod_name, __rank, __file_id, \
+                                __counter, __counter_val, __file_name, \
+                                __mnt_pt, __fs_type) do { \
+    printf("%s\t%" PRId64 "\t%" PRIu64 "\t%s\t%f\t%s\t%s\t%s\n", \
+        __mod_name, __rank, __file_id, __counter, __counter_val, \
+        __file_name, __mnt_pt, __fs_type); \
 } while(0)
 
 /* naive byte swap implementation */
diff --git a/darshan-util/darshan-mpiio-logutils.c b/darshan-util/darshan-mpiio-logutils.c
index d86697b..f96b0a0 100644
--- a/darshan-util/darshan-mpiio-logutils.c
+++ b/darshan-util/darshan-mpiio-logutils.c
@@ -30,14 +30,22 @@ char *mpiio_f_counter_names[] = {
 };
 #undef X
 
-int darshan_log_get_mpiio_file(darshan_fd fd, struct darshan_mpiio_file *file)
+struct darshan_mod_logutil_funcs mpiio_logutils =
+{
+    .log_get_record = &darshan_log_get_mpiio_file,
+    .log_print_record = &darshan_log_print_mpiio_file,
+};
+
+int darshan_log_get_mpiio_file(darshan_fd fd, void **file_rec,
+    darshan_record_id *rec_id)
 {
     int i;
     int ret;
+    struct darshan_mpiio_file *file = NULL;
 
-    /* reset file record, so that diff compares against a zero'd out record
-     * if file is missing
-     */
+    file = malloc(sizeof(*file));
+    if(!file)
+        return(-1);
     memset(file, 0, sizeof(*file));
 
     ret = darshan_log_get_moddat(fd, DARSHAN_MPIIO_MOD,
@@ -56,9 +64,36 @@ int darshan_log_get_mpiio_file(darshan_fd fd, struct darshan_mpiio_file *file)
         }
     }
 
+    /* pass the file record back */
+    *file_rec = (void *)file;
+    *rec_id = file->f_id;
     return(ret);
 }
 
+void darshan_log_print_mpiio_file(void *file_rec, char *file_name,
+    char *mnt_pt, char *fs_type)
+{
+    int i;
+    struct darshan_mpiio_file *mpiio_file_rec =
+        (struct darshan_mpiio_file *)file_rec;
+
+    for(i=0; i<MPIIO_NUM_INDICES; i++)
+    {
+        DARSHAN_COUNTER_PRINT(darshan_module_names[DARSHAN_MPIIO_MOD],
+            mpiio_file_rec->rank, mpiio_file_rec->f_id, mpiio_counter_names[i],
+            mpiio_file_rec->counters[i], file_name, mnt_pt, fs_type);
+    }
+
+    for(i=0; i<MPIIO_F_NUM_INDICES; i++)
+    {
+        DARSHAN_F_COUNTER_PRINT(darshan_module_names[DARSHAN_MPIIO_MOD],
+            mpiio_file_rec->rank, mpiio_file_rec->f_id, mpiio_f_counter_names[i],
+            mpiio_file_rec->fcounters[i], file_name, mnt_pt, fs_type);
+    }
+
+    return;
+}
+
 /*
  * Local variables:
  *  c-indent-level: 4
diff --git a/darshan-util/darshan-mpiio-logutils.h b/darshan-util/darshan-mpiio-logutils.h
index 5c210bc..4a9507f 100644
--- a/darshan-util/darshan-mpiio-logutils.h
+++ b/darshan-util/darshan-mpiio-logutils.h
@@ -13,18 +13,11 @@
 extern char *mpiio_counter_names[];
 extern char *mpiio_f_counter_names[];
 
-int darshan_log_get_mpiio_file(darshan_fd fd, struct darshan_mpiio_file *file);
+extern struct darshan_mod_logutil_funcs mpiio_logutils;
 
-#define MPIIO_COUNTER_PRINT(__file_rec, __counter) do { \
-    printf("%" PRId64 "\t%" PRIu64 "\t%s\t%" PRId64 "\n", \
-        (__file_rec)->rank, (__file_rec)->f_id, mpiio_counter_names[__counter], \
-        (__file_rec)->counters[__counter]); \
-} while(0)
-
-#define MPIIO_F_COUNTER_PRINT(__file_rec, __counter) do { \
-    printf("%" PRId64 "\t%" PRIu64 "\t%s\t%f\n", \
-        (__file_rec)->rank, (__file_rec)->f_id, mpiio_f_counter_names[__counter], \
-        (__file_rec)->fcounters[__counter]); \
-} while(0)
+int darshan_log_get_mpiio_file(darshan_fd fd, void **file_rec,
+    darshan_record_id *rec_id);
+void darshan_log_print_mpiio_file(void *file_rec, char *file_name,
+    char *mnt_pt, char *fs_type);
 
 #endif
diff --git a/darshan-util/darshan-mpiio-parser.c b/darshan-util/darshan-mpiio-parser.c
deleted file mode 100644
index c7db9fa..0000000
--- a/darshan-util/darshan-mpiio-parser.c
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2015 University of Chicago.
- * See COPYRIGHT notice in top-level directory.
- *
- */
-
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <string.h>
-#include <zlib.h>
-#include <time.h>
-#include <stdlib.h>
-#include <getopt.h>
-#include <assert.h>
-
-#include "darshan-logutils.h"
-#include "darshan-mpiio-logutils.h"
-#include "uthash-1.9.2/src/uthash.h"
-
-int main(int argc, char **argv)
-{
-    int ret;
-    int i;
-    char *filename;
-    char tmp_string[4096];
-    darshan_fd fd;
-    struct darshan_header header;
-    struct darshan_job job;
-    struct darshan_record_ref *rec_hash = NULL;
-    struct darshan_record_ref *ref;
-    int mount_count;
-    char** mnt_pts;
-    char** fs_types;
-    time_t tmp_time = 0;
-    char *token;
-    char *save;
-    char buffer[DARSHAN_JOB_METADATA_LEN];
-    struct darshan_mpiio_file next_file;
-
-    assert(argc == 2);
-    filename = argv[1];
-
-    struct stat sbuf;
-    stat(filename, &sbuf);
-
-    fd = darshan_log_open(filename, "r");
-    if(!fd)
-    {
-        fprintf(stderr, "darshan_log_open() failed to open %s\n.", filename);
-        return(-1);
-    }
-
-    /**************************************************************/
-    /* TODO: some of this code should be shared or in a separate executable
-     * instead of repeated in each module parser
-     */
-
-    /* read darshan log header */
-    ret = darshan_log_getheader(fd, &header);
-    if(ret < 0)
-    {
-        fprintf(stderr, "darshan_log_getheader() failed to read log header.\n");
-        darshan_log_close(fd);
-        return(-1);
-    }
-
-    /* read darshan job info */
-    ret = darshan_log_getjob(fd, &job);
-    if(ret < 0)
-    {
-        fprintf(stderr, "darshan_log_getjob() failed to read job data.\n");
-        darshan_log_close(fd);
-        return(-1);
-    }
-
-    /* get the original command line for this job */
-    ret = darshan_log_getexe(fd, tmp_string);
-    if(ret < 0)
-    {
-        fprintf(stderr, "Error: unable to read trailing job information.\n");
-        darshan_log_close(fd);
-        return(-1);
-    }
-
-    /* print job summary */
-    printf("# darshan log version: %s\n", header.version_string);
-    printf("# size of MPIIO file statistics: %zu bytes\n", sizeof(struct darshan_mpiio_file));
-    printf("# size of job statistics: %zu bytes\n", sizeof(struct darshan_job));
-    printf("# exe: %s\n", tmp_string);
-    printf("# uid: %" PRId64 "\n", job.uid);
-    printf("# jobid: %" PRId64 "\n", job.jobid);
-    printf("# start_time: %" PRId64 "\n", job.start_time);
-    tmp_time += job.start_time;
-    printf("# start_time_asci: %s", ctime(&tmp_time));
-    printf("# end_time: %" PRId64 "\n", job.end_time);
-    tmp_time = 0;
-    tmp_time += job.end_time;
-    printf("# end_time_asci: %s", ctime(&tmp_time));
-    printf("# nprocs: %" PRId64 "\n", job.nprocs);
-    printf("# run time: %" PRId64 "\n", job.end_time - job.start_time + 1);
-    for(token=strtok_r(job.metadata, "\n", &save);
-        token != NULL;
-        token=strtok_r(NULL, "\n", &save))
-    {
-        char *key;
-        char *value;
-        /* NOTE: we intentionally only split on the first = character.
-         * There may be additional = characters in the value portion
-         * (for example, when storing mpi-io hints).
-         */
-        strcpy(buffer, token);
-        key = buffer;
-        value = index(buffer, '=');
-        if(!value)
-            continue;
-        /* convert = to a null terminator to split key and value */
-        value[0] = '\0';
-        value++;
-        printf("# metadata: %s = %s\n", key, value);
-    }
-
-    /* get the mount information for this log */
-    ret = darshan_log_getmounts(fd, &mnt_pts, &fs_types, &mount_count);
-    if(ret < 0)
-    {
-        fprintf(stderr, "darshan_log_getmounts() failed to read mount information.\n");
-        darshan_log_close(fd);
-        return(-1);
-    }
-
-    /* print table of mounted file systems */
-    printf("\n# mounted file systems (mount point and fs type)\n");
-    printf("# -------------------------------------------------------\n");
-    for(i=0; i<mount_count; i++)
-    {
-        printf("# mount entry:\t%s\t%s\n", mnt_pts[i], fs_types[i]);
-    }
-
-    /* read hash of darshan records */
-    ret = darshan_log_gethash(fd, &rec_hash);
-    if(ret < 0)
-    {
-        fprintf(stderr, "darshan_log_getmap() failed to read record map.\n");
-        darshan_log_close(fd);
-        return(-1);
-    }
-
-    /* end TODO */
-    /*******************************************/
-
-    ret = darshan_log_get_mpiio_file(fd, &next_file);
-    if(ret < 0)
-    {
-        fprintf(stderr, "darshan_log_get_mpiio_file() failed to read next record.\n");
-        darshan_log_close(fd);
-        return(-1);
-    }
-    if(ret == 0)
-    {
-        printf("# no files opened.\n");
-        darshan_log_close(fd);
-        return(0);
-    }
-   
-    /* loop over each stored MPIIO file record and print counters */
-    do
-    {
-        /* get the pathname for this record */
-        HASH_FIND(hlink, rec_hash, &next_file.f_id, sizeof(darshan_record_id), ref);
-        assert(ref);
-
-        for(i=0; i<MPIIO_NUM_INDICES; i++)
-        {
-            MPIIO_COUNTER_PRINT(&next_file, i);
-        }
-        for(i=0; i<MPIIO_F_NUM_INDICES; i++)
-        {
-            MPIIO_F_COUNTER_PRINT(&next_file, i);
-        }
-    } while((ret = darshan_log_get_mpiio_file(fd, &next_file)) == 1);
-
-    /* free mount info */
-    for(i=0; i<mount_count; i++)
-    {
-        free(mnt_pts[i]);
-        free(fs_types[i]);
-    }
-    if(mount_count > 0)
-    {
-        free(mnt_pts);
-        free(fs_types);
-    }
-
-    darshan_log_close(fd);
-
-    return(0);
-}
diff --git a/darshan-util/darshan-parser.c b/darshan-util/darshan-parser.c
index 02171a5..dd211a9 100644
--- a/darshan-util/darshan-parser.c
+++ b/darshan-util/darshan-parser.c
@@ -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.
+ *
  */
 
 #include <stdio.h>
@@ -15,241 +16,88 @@
 #include <getopt.h>
 #include <assert.h>
 
-#include "darshan-logutils.h"
-
 #include "uthash-1.9.2/src/uthash.h"
 
-/*
- * Options
- */
-#define OPTION_BASE  (1 << 0)  /* darshan log fields */
-#define OPTION_TOTAL (1 << 1)  /* aggregated fields */
-#define OPTION_PERF  (1 << 2)  /* derived performance */
-#define OPTION_FILE  (1 << 3)  /* file count totals */
-#define OPTION_FILE_LIST  (1 << 4)  /* per-file summaries */
-#define OPTION_FILE_LIST_DETAILED  (1 << 6)  /* per-file summaries with extra detail */
-#define OPTION_ALL (\
-  OPTION_BASE|\
-  OPTION_TOTAL|\
-  OPTION_PERF|\
-  OPTION_FILE|\
-  OPTION_FILE_LIST|\
-  OPTION_FILE_LIST_DETAILED)
-
-#define FILETYPE_SHARED (1 << 0)
-#define FILETYPE_UNIQUE (1 << 1)
-#define FILETYPE_PARTSHARED (1 << 2)
-
-#define max(a,b) (((a) > (b)) ? (a) : (b))
-#define max3(a,b,c) (((a) > (b)) ? (((a) > (c)) ? (a) : (c)) : (((b) > (c)) ? (b) : (c)))
-
-/*
- * Datatypes
- */
-typedef struct hash_entry_s
-{
-    UT_hash_handle hlink;
-    uint64_t hash;
-    int64_t type;
-    int64_t procs;
-    int64_t counters[CP_NUM_INDICES];
-    double  fcounters[CP_F_NUM_INDICES];
-    double cumul_time;
-    double meta_time;
-    double slowest_time;
-    char name_suffix[CP_NAME_SUFFIX_LEN+1];
-} hash_entry_t;
-
-typedef struct perf_data_s
-{
-    int64_t total_bytes;
-    double slowest_rank_time;
-    double slowest_rank_meta_time;
-    int slowest_rank_rank;
-    double shared_time_by_cumul;
-    double shared_time_by_open;
-    double shared_time_by_open_lastio;
-    double shared_time_by_slowest;
-    double shared_meta_time;
-    double agg_perf_by_cumul;
-    double agg_perf_by_open;
-    double agg_perf_by_open_lastio;
-    double agg_perf_by_slowest;
-    double *rank_cumul_io_time;
-    double *rank_cumul_md_time;
-} perf_data_t;
-
-typedef struct file_data_s
-{
-    int64_t total;
-    int64_t total_size;
-    int64_t total_max;
-    int64_t read_only;
-    int64_t read_only_size;
-    int64_t read_only_max;
-    int64_t write_only;
-    int64_t write_only_size;
-    int64_t write_only_max;
-    int64_t read_write;
-    int64_t read_write_size;
-    int64_t read_write_max;
-    int64_t unique;
-    int64_t unique_size;
-    int64_t unique_max;
-    int64_t shared;
-    int64_t shared_size;
-    int64_t shared_max;
-} file_data_t;
-
-/*
- * Prototypes
- */
-void accum_perf(struct darshan_file *, hash_entry_t *, perf_data_t *);
-void calc_perf(struct darshan_job *, hash_entry_t *, perf_data_t *);
-
-void accum_file(struct darshan_job *, struct darshan_file *, hash_entry_t *, file_data_t *);
-void calc_file(struct darshan_job *, hash_entry_t *, file_data_t *);
-void file_list(struct darshan_job *, hash_entry_t *, int);
-
-int usage (char *exename)
-{
-    fprintf(stderr, "Usage: %s [options] <filename>\n", exename);
-    fprintf(stderr, "    --all   : all sub-options are enabled\n");
-    fprintf(stderr, "    --base  : darshan log field data [default]\n");
-    fprintf(stderr, "    --file  : total file counts\n");
-    fprintf(stderr, "    --file-list  : per-file summaries\n");
-    fprintf(stderr, "    --file-list-detailed  : per-file summaries with additional detail\n");
-    fprintf(stderr, "    --perf  : derived perf data\n");
-    fprintf(stderr, "    --total : aggregated darshan field data\n");
-
-    exit(1);
-}
-
-int parse_args (int argc, char **argv, char **filename)
-{
-    int index;
-    int mask;
-    static struct option long_opts[] =
-    {
-        {"all",   0, NULL, OPTION_ALL},
-        {"base",  0, NULL, OPTION_BASE},
-        {"file",  0, NULL, OPTION_FILE},
-        {"file-list",  0, NULL, OPTION_FILE_LIST},
-        {"file-list-detailed",  0, NULL, OPTION_FILE_LIST_DETAILED},
-        {"perf",  0, NULL, OPTION_PERF},
-        {"total", 0, NULL, OPTION_TOTAL},
-        {"help",  0, NULL, 0},
-        {0, 0, 0, 0}
-    };
-
-    mask = 0;
-
-    while(1)
-    {
-        int c = getopt_long(argc, argv, "", long_opts, &index);
-
-        if (c == -1) break;
-
-        switch(c)
-        {
-            case OPTION_ALL:
-            case OPTION_BASE:
-            case OPTION_FILE:
-            case OPTION_FILE_LIST:
-            case OPTION_FILE_LIST_DETAILED:
-            case OPTION_PERF:
-            case OPTION_TOTAL:
-                mask |= c;
-                break;
-            case 0:
-            case '?':
-            default:
-                usage(argv[0]);
-                break;
-        }
-    }
-
-    if (optind < argc)
-    {
-        *filename = argv[optind];
-    }
-    else
-    {
-        usage(argv[0]);
-    }
-
-    /* default mask value if none specified */
-    if (mask == 0)
-    {
-        mask = OPTION_BASE;
-    }
-
-    return mask;
-}
+#include "darshan-logutils.h"
 
 int main(int argc, char **argv)
 {
     int ret;
-    int mask;
+    int i, j;
     char *filename;
-    struct darshan_job job;
-    struct darshan_file cp_file;
     char tmp_string[4096];
-    time_t tmp_time = 0;
-    darshan_fd file;
-    int i;
+    darshan_fd fd;
+    struct darshan_header header;
+    struct darshan_job job;
+    struct darshan_record_ref *rec_hash = NULL;
+    struct darshan_record_ref *ref;
     int mount_count;
-    int64_t* devs;
     char** mnt_pts;
     char** fs_types;
-    int last_rank = 0;
+    time_t tmp_time = 0;
     char *token;
     char *save;
     char buffer[DARSHAN_JOB_METADATA_LEN];
+    int empty_mods = 0;
 
-    hash_entry_t *file_hash = NULL;
-    hash_entry_t *curr = NULL;
-    hash_entry_t *tmp = NULL;
-    hash_entry_t total;
-    perf_data_t pdata;
-    file_data_t fdata;
-
-    memset(&pdata, 0, sizeof(pdata));
-    memset(&total, 0, sizeof(total));
+    /* TODO: argument parsing */
+    assert(argc == 2);
+    filename = argv[1];
 
-    mask = parse_args(argc, argv, &filename);
-
-    file = darshan_log_open(filename, "r");
-    if(!file)
+    fd = darshan_log_open(filename, "r");
+    if(!fd)
     {
         fprintf(stderr, "darshan_log_open() failed to open %s\n.", filename);
         return(-1);
     }
-   
-    /* read job info */
-    ret = darshan_log_getjob(file, &job);
+
+    /* read darshan log header */
+    ret = darshan_log_getheader(fd, &header);
     if(ret < 0)
     {
-        fprintf(stderr, "Error: unable to read job information from log file.\n");
-        darshan_log_close(file);
+        fprintf(stderr, "darshan_log_getheader() failed to read log header.\n");
+        darshan_log_close(fd);
         return(-1);
     }
 
-    /* warn user about any missing information in this log format */
-    darshan_log_print_version_warnings(&job);
+    /* read darshan job info */
+    ret = darshan_log_getjob(fd, &job);
+    if(ret < 0)
+    {
+        fprintf(stderr, "darshan_log_getjob() failed to read job data.\n");
+        darshan_log_close(fd);
+        return(-1);
+    }
 
-    ret = darshan_log_getexe(file, tmp_string);
+    /* get the original command line for this job */
+    ret = darshan_log_getexe(fd, tmp_string);
     if(ret < 0)
     {
         fprintf(stderr, "Error: unable to read trailing job information.\n");
-        darshan_log_close(file);
+        darshan_log_close(fd);
+        return(-1);
+    }
+
+    /* get the mount information for this log */
+    ret = darshan_log_getmounts(fd, &mnt_pts, &fs_types, &mount_count);
+    if(ret < 0)
+    {
+        fprintf(stderr, "darshan_log_getmounts() failed to read mount information.\n");
+        darshan_log_close(fd);
+        return(-1);
+    }
+
+    /* read hash of darshan records */
+    ret = darshan_log_gethash(fd, &rec_hash);
+    if(ret < 0)
+    {
+        fprintf(stderr, "darshan_log_getmap() failed to read record map.\n");
+        darshan_log_close(fd);
         return(-1);
     }
 
     /* print job summary */
-    printf("# darshan log version: %s\n", job.version_string);
-    printf("# size of file statistics: %zu bytes\n", sizeof(cp_file));
-    printf("# size of job statistics: %zu bytes\n", sizeof(job));
+    printf("# darshan log version: %s\n", header.version_string);
     printf("# exe: %s\n", tmp_string);
     printf("# uid: %" PRId64 "\n", job.uid);
     printf("# jobid: %" PRId64 "\n", job.jobid);
@@ -282,812 +130,115 @@ int main(int argc, char **argv)
         value++;
         printf("# metadata: %s = %s\n", key, value);
     }
- 
-    /* print table of mounted file systems */
-    ret = darshan_log_getmounts(file, &devs, &mnt_pts, &fs_types, &mount_count);
-    printf("\n# mounted file systems (device, mount point, and fs type)\n");
-    printf("# -------------------------------------------------------\n");
-    for(i=0; i<mount_count; i++)
-    {
-        printf("# mount entry: %" PRId64 "\t%s\t%s\n", devs[i], mnt_pts[i], fs_types[i]);
-    }
-  
-    /* try to retrieve first record (may not exist) */
-    ret = darshan_log_getfile(file, &job, &cp_file);
-    if(ret < 0)
-    {
-        fprintf(stderr, "Error: failed to parse log file.\n");
-        fflush(stderr);
-        return(-1);
-    }
-    if(ret == 0)
-    {
-        /* it looks like the app didn't open any files */
-        printf("# no files opened.\n");
-        darshan_log_close(file);
-        return(0);
-    }
-
-    if ((mask & OPTION_BASE))
-    {
-        printf("\n# description of columns:\n");
-        printf("#   <rank>: MPI rank.  -1 indicates that the file is shared\n");
-        printf("#      across all processes and statistics are aggregated.\n");
-        printf("#   <file>: hash of file path.  0 indicates that statistics\n");
-        printf("#      are condensed to refer to all files opened at the given\n");
-        printf("#      process.\n");
-        printf("#   <counter> and <value>: statistical counters.\n");
-        printf("#      A value of -1 indicates that Darshan could not monitor\n");
-        printf("#      that counter, and its value should be ignored.\n");
-        printf("#   <name suffix>: last %d characters of file name.\n", CP_NAME_SUFFIX_LEN);
-        printf("#   <mount pt>: mount point that the file resides on.\n");
-        printf("#   <fs type>: type of file system that the file resides on.\n");
-        printf("\n# description of counters:\n");
-        printf("#   CP_POSIX_*: posix operation counts.\n");
-        printf("#   CP_COLL_*: MPI collective operation counts.\n");
-        printf("#   CP_INDEP_*: MPI independent operation counts.\n");
-        printf("#   CP_SPIT_*: MPI split collective operation counts.\n");
-        printf("#   CP_NB_*: MPI non blocking operation counts.\n");
-        printf("#   READS,WRITES,OPENS,SEEKS,STATS, and MMAPS are types of operations.\n");
-        printf("#   CP_*_NC_OPENS: number of indep. and collective pnetcdf opens.\n");
-        printf("#   CP_HDF5_OPENS: number of hdf5 opens.\n");
-        printf("#   CP_COMBINER_*: combiner counts for MPI mem and file datatypes.\n");
-        printf("#   CP_HINTS: number of times MPI hints were used.\n");
-        printf("#   CP_VIEWS: number of times MPI file views were used.\n");
-        printf("#   CP_MODE: mode that file was opened in.\n");
-        printf("#   CP_BYTES_*: total bytes read and written.\n");
-        printf("#   CP_MAX_BYTE_*: highest offset byte read and written.\n");
-        printf("#   CP_CONSEC_*: number of exactly adjacent reads and writes.\n");
-        printf("#   CP_SEQ_*: number of reads and writes from increasing offsets.\n");
-        printf("#   CP_RW_SWITCHES: number of times access alternated between read and write.\n");
-        printf("#   CP_*_ALIGNMENT: memory and file alignment.\n");
-        printf("#   CP_*_NOT_ALIGNED: number of reads and writes that were not aligned.\n");
-        printf("#   CP_MAX_*_TIME_SIZE: size of the slowest read and write operations.\n");
-        printf("#   CP_SIZE_READ_*: histogram of read access sizes.\n");
-        printf("#   CP_SIZE_READ_AGG_*: histogram of MPI datatype total sizes.\n");
-        printf("#   CP_EXTENT_READ_*: histogram of MPI datatype extents.\n");
-        printf("#   CP_STRIDE*_STRIDE: the four most common strides detected.\n");
-        printf("#   CP_STRIDE*_COUNT: count of the four most common strides.\n");
-        printf("#   CP_ACCESS*_ACCESS: the four most common access sizes.\n");
-        printf("#   CP_ACCESS*_COUNT: count of the four most common access sizes.\n");
-        printf("#   CP_DEVICE: File system identifier.\n");
-        printf("#   CP_SIZE_AT_OPEN: size of file when first opened.\n");
-        printf("#   CP_*_RANK_BYTES: fastest, slowest and variance of bytes transfer.\n");
-        printf("#   CP_F_OPEN_TIMESTAMP: timestamp of first open (mpi or posix).\n");
-        printf("#   CP_F_*_START_TIMESTAMP: timestamp of first read/write (mpi or posix).\n");
-        printf("#   CP_F_*_END_TIMESTAMP: timestamp of last read/write (mpi or posix).\n");
-        printf("#   CP_F_CLOSE_TIMESTAMP: timestamp of last close (mpi or posix).\n");
-        printf("#   CP_F_POSIX_READ/WRITE_TIME: cumulative time spent in posix reads or writes.\n");
-        printf("#   CP_F_MPI_READ/WRITE_TIME: cumulative time spent in mpi-io reads or writes.\n");
-        printf("#   CP_F_POSIX_META_TIME: cumulative time spent in posix open, close, fsync, stat and seek, .\n");
-        printf("#   CP_F_MPI_META_TIME: cumulative time spent in mpi-io open, close, set_view, and sync.\n");
-        printf("#   CP_MAX_*_TIME: duration of the slowest read and write operations.\n");
-        printf("#   CP_*_RANK_TIME: fastest, slowest variance of transfer time. Note that these counters show MPI-IO time for files accessed using MPI-IO, and POSIX time otherwise.\n");
-
-        printf("\n");
-        CP_PRINT_HEADER();
-    }
-
-    pdata.rank_cumul_io_time = malloc(sizeof(double)*job.nprocs);
-    pdata.rank_cumul_md_time = malloc(sizeof(double)*job.nprocs);
-    if (!pdata.rank_cumul_io_time || !pdata.rank_cumul_md_time)
-    {
-        perror("malloc failed");
-        darshan_log_close(file);
-        return(-1);
-    }
-    else
-    {
-        memset(pdata.rank_cumul_io_time, 0, sizeof(double)*job.nprocs);
-        memset(pdata.rank_cumul_md_time, 0, sizeof(double)*job.nprocs);
-    }
-
-    do
-    {
-        char* mnt_pt = NULL;
-        char* fs_type = NULL;
-        hash_entry_t *hfile = NULL;
-
-        if(cp_file.rank != -1 && cp_file.rank < last_rank)
-        {
-            fprintf(stderr, "Error: log file contains out of order rank data.\n");
-            fflush(stderr);
-            return(-1);
-        }
-        if(cp_file.rank != -1)
-            last_rank = cp_file.rank;
-        
-        for(i=0; i<mount_count; i++)
-        {
-            if(cp_file.counters[CP_DEVICE] == devs[i])
-            {
-                mnt_pt = mnt_pts[i];
-                fs_type = fs_types[i];
-                break;
-            }
-        }
-        if(!mnt_pt)
-            mnt_pt = "UNKNOWN";
-        if(!fs_type)
-            fs_type = "UNKNOWN";
-
-        HASH_FIND(hlink,file_hash,&cp_file.hash,sizeof(int64_t),hfile);
-        if (!hfile)
-        {
-            hfile = (hash_entry_t*) malloc(sizeof(*hfile));
-            if (!hfile)
-            {
-                fprintf(stderr,"malloc failure");
-                exit(1);
-            }
-
-            /* init */
-            memset(hfile, 0, sizeof(*hfile));
-            hfile->hash          = cp_file.hash;
-            memcpy(hfile->name_suffix, cp_file.name_suffix, CP_NAME_SUFFIX_LEN+1);
-            hfile->type          = 0;
-            hfile->procs         = 0;
-            hfile->cumul_time    = 0.0;
-            hfile->meta_time     = 0.0;
-            hfile->slowest_time  = 0.0;
-
-            HASH_ADD(hlink,file_hash,hash,sizeof(int64_t),hfile);
-        }
-
-        accum_file(&job, &cp_file, &total, NULL);
-        accum_file(&job, &cp_file, hfile, &fdata);
-        accum_perf(&cp_file, hfile, &pdata);
 
-        if ((mask & OPTION_BASE))
-        {
-            for(i=0; i<CP_NUM_INDICES; i++)
-            {
-                CP_PRINT(&job, &cp_file, i, mnt_pt, fs_type);
-            }
-            for(i=0; i<CP_F_NUM_INDICES; i++)
-            {
-                CP_F_PRINT(&job, &cp_file, i, mnt_pt, fs_type);
-            }
-        }
-    }while((ret = darshan_log_getfile(file, &job, &cp_file)) == 1);
-
-    /* Total Calc */
-    if ((mask & OPTION_TOTAL))
+    /* print breakdown of each log file component's contribution to file size */
+    printf("\n# log file component sizes (decompressed)\n");
+    printf("# -------------------------------------------------------\n");
+    printf("# header: %zu bytes\n", sizeof(struct darshan_header));
+    printf("# job data: %zu bytes\n", sizeof(struct darshan_job));
+    printf("# record table: %zu bytes\n", header.rec_map.len);
+    for(i=0; i<DARSHAN_MAX_MODS; i++)
     {
-        for(i=0; i<CP_NUM_INDICES; i++)
-        {
-            printf("total_%s: %" PRId64 "\n",
-                   darshan_names[i], total.counters[i]);
-        }
-        for(i=0; i<CP_F_NUM_INDICES; i++)
+        if(header.mod_map[i].len)
         {
-            printf("total_%s: %lf\n",
-                   darshan_f_names[i], total.fcounters[i]);
+            printf("# %s module: %zu bytes\n", darshan_module_names[i],
+                header.mod_map[i].len);
         }
     }
 
-    /* Perf Calc */
-    calc_perf(&job, file_hash, &pdata);
-    if ((mask & OPTION_PERF))
-    {
-        printf("\n# performance\n");
-        printf("# -----------\n");
-        printf("# total_bytes: %" PRId64 "\n", pdata.total_bytes);
-        printf("#\n");
-        printf("# I/O timing for unique files (seconds):\n");
-        printf("# ...........................\n");
-        printf("# unique files: slowest_rank_io_time: %lf\n", pdata.slowest_rank_time);
-        printf("# unique files: slowest_rank_meta_time: %lf\n", pdata.slowest_rank_meta_time);
-        printf("# unique files: slowest rank: %d\n", pdata.slowest_rank_rank);
-        printf("#\n");
-        printf("# I/O timing for shared files (seconds):\n");
-        printf("# (multiple estimates shown; time_by_slowest is generally the most accurate)\n");
-        printf("# ...........................\n");
-        printf("# shared files: time_by_cumul_io_only: %lf\n", pdata.shared_time_by_cumul);
-        printf("# shared files: time_by_cumul_meta_only: %lf\n", pdata.shared_meta_time);
-        printf("# shared files: time_by_open: %lf\n", pdata.shared_time_by_open);
-        printf("# shared files: time_by_open_lastio: %lf\n", pdata.shared_time_by_open_lastio);
-        printf("# shared files: time_by_slowest: %lf\n", pdata.shared_time_by_slowest);
-        printf("#\n");
-        printf("# Aggregate performance, including both shared and unique files (MiB/s):\n");
-        printf("# (multiple estimates shown; agg_perf_by_slowest is generally the most accurate)\n");
-        printf("# ...........................\n");
-        printf("# agg_perf_by_cumul: %lf\n", pdata.agg_perf_by_cumul);
-        printf("# agg_perf_by_open: %lf\n", pdata.agg_perf_by_open);
-        printf("# agg_perf_by_open_lastio: %lf\n", pdata.agg_perf_by_open_lastio);
-        printf("# agg_perf_by_slowest: %lf\n", pdata.agg_perf_by_slowest);
-    }
-
-    /* File Calc */
-    calc_file(&job, file_hash, &fdata);
-    if ((mask & OPTION_FILE))
-    {
-        printf("\n# files\n");
-        printf("# -----\n");
-        printf("# total: %" PRId64 " %" PRId64 " %" PRId64 "\n",
-               fdata.total,
-               fdata.total_size,
-               fdata.total_max);
-        printf("# read_only: %" PRId64 " %" PRId64 " %" PRId64 "\n",
-               fdata.read_only,
-               fdata.read_only_size,
-               fdata.read_only_max);
-        printf("# write_only: %" PRId64 " %" PRId64 " %" PRId64 "\n",
-               fdata.write_only,
-               fdata.write_only_size,
-               fdata.write_only_max);
-        printf("# read_write: %" PRId64 " %" PRId64 " %" PRId64 "\n",
-               fdata.read_write,
-               fdata.read_write_size,
-               fdata.read_write_max);
-        printf("# unique: %" PRId64 " %" PRId64 " %" PRId64 "\n",
-               fdata.unique,
-               fdata.unique_size,
-               fdata.unique_max);
-        printf("# shared: %" PRId64 " %" PRId64 " %" PRId64 "\n",
-               fdata.shared,
-               fdata.shared_size,
-               fdata.shared_max);
-    }
-
-    if ((mask & OPTION_FILE_LIST) || mask & OPTION_FILE_LIST_DETAILED)
-    {
-        if(mask & OPTION_FILE_LIST_DETAILED)
-            file_list(&job, file_hash, 1);
-        else
-            file_list(&job, file_hash, 0);
-    }
-
-    if(ret < 0)
-    {
-        fprintf(stderr, "Error: failed to parse log file.\n");
-        fflush(stderr);
-        return(-1);
-    }
-
+    /* print table of mounted file systems */
+    printf("\n# mounted file systems (mount point and fs type)\n");
+    printf("# -------------------------------------------------------\n");
     for(i=0; i<mount_count; i++)
     {
-        free(mnt_pts[i]);
-        free(fs_types[i]);
-    }
-    if(mount_count > 0)
-    {
-        free(devs);
-        free(mnt_pts);
-        free(fs_types);
+        printf("# mount entry:\t%s\t%s\n", mnt_pts[i], fs_types[i]);
     }
- 
-    darshan_log_close(file);
 
-    HASH_ITER(hlink, file_hash, curr, tmp)
-    {
-        HASH_DELETE(hlink, file_hash, curr);
-        free(curr);
-    }
+    DARSHAN_PRINT_HEADER();
 
-    return(0);
-}
-
-void accum_file(struct darshan_job *job,
-                struct darshan_file *dfile,
-                hash_entry_t *hfile, 
-                file_data_t *fdata)
-{
-    int i;
-
-    hfile->procs += 1;
-
-    if (dfile->rank == -1)
-    {
-        if(job->version_string[0] == '1')
-        {
-            hfile->slowest_time = 
-                max((dfile->fcounters[CP_F_READ_END_TIMESTAMP] 
-                    - dfile->fcounters[CP_F_OPEN_TIMESTAMP]),
-                    (dfile->fcounters[CP_F_WRITE_END_TIMESTAMP] 
-                    - dfile->fcounters[CP_F_OPEN_TIMESTAMP]));
-            if(hfile->slowest_time < 0)
-                hfile->slowest_time = 0;
-        }
-        else
-        {
-            hfile->slowest_time = dfile->fcounters[CP_F_SLOWEST_RANK_TIME];
-        }
-    }
-    else
+    for(i=0; i<DARSHAN_MAX_MODS; i++)
     {
-        if(dfile->counters[CP_INDEP_OPENS] || dfile->counters[CP_COLL_OPENS])
-        {
-            /* MPI file */
-            hfile->slowest_time = max(hfile->slowest_time, 
-                (dfile->fcounters[CP_F_MPI_META_TIME] +
-                dfile->fcounters[CP_F_MPI_READ_TIME] +
-                dfile->fcounters[CP_F_MPI_WRITE_TIME]));
-        }
-        else
-        {
-            /* POSIX file */
-            hfile->slowest_time = max(hfile->slowest_time, 
-                (dfile->fcounters[CP_F_POSIX_META_TIME] +
-                dfile->fcounters[CP_F_POSIX_READ_TIME] +
-                dfile->fcounters[CP_F_POSIX_WRITE_TIME]));
-        }
-    }
+        void *rec_p = NULL;
+        darshan_record_id rec_id;
 
-    if (dfile->rank == -1)
-    {
-        hfile->procs = job->nprocs;
-        hfile->type |= FILETYPE_SHARED;
-
-    }
-    else if (hfile->procs > 1)
-    {
-        hfile->type &= (~FILETYPE_UNIQUE);
-        hfile->type |= FILETYPE_PARTSHARED;
-    }
-    else
-    {
-        hfile->type |= FILETYPE_UNIQUE;
-    }
-
-    if(dfile->counters[CP_INDEP_OPENS] || dfile->counters[CP_COLL_OPENS])
-    {
-        hfile->cumul_time += dfile->fcounters[CP_F_MPI_META_TIME] +
-                             dfile->fcounters[CP_F_MPI_READ_TIME] +
-                             dfile->fcounters[CP_F_MPI_WRITE_TIME];
-    }
-    else
-    {
-        hfile->cumul_time += dfile->fcounters[CP_F_POSIX_META_TIME] +
-                             dfile->fcounters[CP_F_POSIX_READ_TIME] +
-                             dfile->fcounters[CP_F_POSIX_WRITE_TIME];
-    }
-
-    for (i = 0; i < CP_NUM_INDICES; i++)
-    {
-        switch(i)
+        /* skip modules not present in log file */
+        if(header.mod_map[i].len == 0)
         {
-        case CP_DEVICE:
-        case CP_MODE:
-        case CP_MEM_ALIGNMENT:
-        case CP_FILE_ALIGNMENT:
-            if(CP_FILE_PARTIAL(hfile))
-                hfile->counters[i] = dfile->counters[i];
-            break;
-        case CP_SIZE_AT_OPEN:
-            if (hfile->counters[i] == -1)
-            {
-                hfile->counters[i] = dfile->counters[i];
-            }
-            if (hfile->counters[i] > dfile->counters[i] && !CP_FILE_PARTIAL(dfile))
-            {
-                hfile->counters[i] = dfile->counters[i];
-            }
-            break;
-        case CP_MAX_BYTE_READ:
-        case CP_MAX_BYTE_WRITTEN:
-            if (hfile->counters[i] < dfile->counters[i])
-            {
-                hfile->counters[i] = dfile->counters[i];
-            }
-            break;
+            empty_mods++;
+            if(empty_mods == DARSHAN_MAX_MODS)
+                printf("# no module data available.\n");
 
-        case CP_STRIDE1_STRIDE:
-        case CP_STRIDE2_STRIDE:
-        case CP_STRIDE3_STRIDE:
-        case CP_STRIDE4_STRIDE:
-        case CP_ACCESS1_ACCESS:
-        case CP_ACCESS2_ACCESS:
-        case CP_ACCESS3_ACCESS:
-        case CP_ACCESS4_ACCESS:
-           /*
-            * do nothing here because these will be stored
-            * when the _COUNT is accessed.
-            */
-           break;
- 
-        case CP_STRIDE1_COUNT:
-        case CP_STRIDE2_COUNT:
-        case CP_STRIDE3_COUNT:
-        case CP_STRIDE4_COUNT:
-        case CP_ACCESS1_COUNT:
-        case CP_ACCESS2_COUNT:
-        case CP_ACCESS3_COUNT:
-        case CP_ACCESS4_COUNT:
-            if (hfile->counters[i] < dfile->counters[i])
-            {
-                hfile->counters[i]   = dfile->counters[i];
-                hfile->counters[i-4] = dfile->counters[i-4];
-            }
-            break;
-        case CP_FASTEST_RANK:
-        case CP_SLOWEST_RANK:
-        case CP_FASTEST_RANK_BYTES:
-        case CP_SLOWEST_RANK_BYTES:
-            hfile->counters[i] = 0;
-            break;
-        case CP_MAX_READ_TIME_SIZE:
-        case CP_MAX_WRITE_TIME_SIZE:
-            break;
-        default:
-            hfile->counters[i] += dfile->counters[i];
-            break;
+            continue;
         }
-    }
 
-    for (i = 0; i < CP_F_NUM_INDICES; i++)
-    {
-        switch(i)
+        /* skip modules with no defined logutil handlers */
+        if(!mod_logutils[i])
         {
-            case CP_F_OPEN_TIMESTAMP:
-            case CP_F_READ_START_TIMESTAMP:
-            case CP_F_WRITE_START_TIMESTAMP:
-                if(hfile->fcounters[i] == 0 || 
-                    hfile->fcounters[i] > dfile->fcounters[i])
-                {
-                    hfile->fcounters[i] = dfile->fcounters[i];
-                }
-                break;
-            case CP_F_CLOSE_TIMESTAMP:
-            case CP_F_READ_END_TIMESTAMP:
-            case CP_F_WRITE_END_TIMESTAMP:
-                if(hfile->fcounters[i] == 0 || 
-                    hfile->fcounters[i] < dfile->fcounters[i])
-                {
-                    hfile->fcounters[i] = dfile->fcounters[i];
-                }
-                break;
-            case CP_F_FASTEST_RANK_TIME:
-            case CP_F_SLOWEST_RANK_TIME:
-            case CP_F_VARIANCE_RANK_TIME:
-            case CP_F_VARIANCE_RANK_BYTES:
-                hfile->fcounters[i] = 0;
-                break;
-            case CP_F_MAX_READ_TIME:
-                if (hfile->fcounters[i] > dfile->fcounters[i])
-                {
-                    hfile->fcounters[i] = dfile->fcounters[i];
-                    hfile->counters[CP_MAX_READ_TIME_SIZE] =
-                        dfile->counters[CP_MAX_READ_TIME_SIZE];
-                }
-                break;
-            case CP_F_MAX_WRITE_TIME:
-                if (hfile->fcounters[i] > dfile->fcounters[i])
-                {
-                    hfile->fcounters[i] = dfile->fcounters[i];
-                    hfile->counters[CP_MAX_WRITE_TIME_SIZE] =
-                        dfile->counters[CP_MAX_WRITE_TIME_SIZE];
-                }
-                break;
-            default:
-                hfile->fcounters[i] += dfile->fcounters[i];
-                break;
+            fprintf(stderr, "Warning: no log utility handlers defined "
+                "for module %s, SKIPPING\n", darshan_module_names[i]);
+            continue;
         }
-    }
 
-    return;
-}
+        /* this module has data to be parsed and printed */
 
-void file_list(struct darshan_job *djob, hash_entry_t *file_hash, int detail_flag)
-{
-    hash_entry_t *curr = NULL;
-    hash_entry_t *tmp = NULL;
-    char* type;
-    int i;
-
-    /* TODO: list of columns:
-     *
-     * normal mode?
-     * - hash
-     * - suffix
-     * - MPI or POSIX
-     * - nprocs
-     * - slowest I/O time
-     * - average cumulative I/O time
-     *
-     * detailed mode?
-     * - first open
-     * - first read
-     * - first write
-     * - last close
-     * - last read
-     * - last write
-     * - MPI indep opens
-     * - MPI coll opens
-     * - POSIX opens
-     * - r histogram (POSIX)
-     * - w histogram (POSIX)
-     */
-
-    if(detail_flag)
-        printf("\n# Per-file summary of I/O activity (detailed).\n");
-    else
-        printf("\n# Per-file summary of I/O activity.\n");
+        /* TODO: do modules print header of what each counter means??? */
 
-    printf("# <hash>: hash of file name\n");
-    printf("# <suffix>: last %d characters of file name\n", CP_NAME_SUFFIX_LEN);
-    printf("# <type>: MPI or POSIX\n");
-    printf("# <nprocs>: number of processes that opened the file\n");
-    printf("# <slowest>: (estimated) time in seconds consumed in IO by slowest process\n");
-    printf("# <avg>: average time in seconds consumed in IO per process\n");
-    if(detail_flag)
-    {
-        printf("# <start_{open/read/write}>: start timestamp of first open, read, or write\n");
-        printf("# <end_{open/read/write}>: end timestamp of last open, read, or write\n");
-        printf("# <mpi_indep_opens>: independent MPI_File_open calls\n");
-        printf("# <mpi_coll_opens>: collective MPI_File_open calls\n");
-        printf("# <posix_opens>: POSIX open calls\n");
-        printf("# <CP_SIZE_READ_*>: POSIX read size histogram\n");
-        printf("# <CP_SIZE_WRITE_*>: POSIX write size histogram\n");
-    }
-    
-    printf("\n# <hash>\t<suffix>\t<type>\t<nprocs>\t<slowest>\t<avg>");
-    if(detail_flag)
-    {
-        printf("\t<start_open>\t<start_read>\t<start_write>");
-        printf("\t<end_open>\t<end_read>\t<end_write>");
-        printf("\t<mpi_indep_opens>\t<mpi_coll_opens>\t<posix_opens>");
-        for(i=CP_SIZE_READ_0_100; i<= CP_SIZE_WRITE_1G_PLUS; i++)
-            printf("\t%s", darshan_names[i]);
-    }
-    printf("\n");
+        /* loop over each of this module's records and print them */
+        while ((ret = mod_logutils[i]->log_get_record(fd, &rec_p, &rec_id)) == 1)
+        {
+            char *mnt_pt = NULL;
+            char *fs_type = NULL;
 
-    HASH_ITER(hlink, file_hash, curr, tmp)
-    {
-        if(curr->counters[CP_INDEP_OPENS] || curr->counters[CP_COLL_OPENS])
-            type = "MPI";
-        else
-            type = "POSIX";
+            /* get the pathname for this record */
+            HASH_FIND(hlink, rec_hash, &rec_id, sizeof(darshan_record_id), ref);
+            assert(ref);
 
-        printf("%" PRIu64 "\t%s\t%s\t%" PRId64 "\t%f\t%f",
-            curr->hash,
-            curr->name_suffix,
-            type,
-            curr->procs,
-            curr->slowest_time,
-            curr->cumul_time/(double)curr->procs);
-        if(detail_flag)
-        {
-            for(i=CP_F_OPEN_TIMESTAMP; i<=CP_F_WRITE_END_TIMESTAMP; i++)
+            /* get mount point and fs type associated with this record */
+            for(j=0; j<mount_count; j++)
             {
-                printf("\t%f", curr->fcounters[i]);
+                if(strncmp(mnt_pts[j], ref->rec.name, strlen(mnt_pts[j])) == 0)
+                {
+                    mnt_pt = mnt_pts[j];
+                    fs_type = fs_types[j];
+                    break;
+                }
             }
-            printf("\t%" PRId64 "\t%" PRId64 "\t%" PRId64, curr->counters[CP_INDEP_OPENS], curr->counters[CP_COLL_OPENS], curr->counters[CP_POSIX_OPENS]);
-            for(i=CP_SIZE_READ_0_100; i<= CP_SIZE_WRITE_1G_PLUS; i++)
-                printf("\t%" PRId64, curr->counters[i]);
-        }
-        printf("\n");
-    }
-
-    return;
-}
+            if(!mnt_pt)
+                mnt_pt = "UNKNOWN";
+            if(!fs_type)
+                fs_type = "UNKNOWN";
 
-void calc_file(struct darshan_job *djob,
-               hash_entry_t *file_hash, 
-               file_data_t *fdata)
-{
-    hash_entry_t *curr = NULL;
-    hash_entry_t *tmp = NULL;
+            /* print the corresponding module data for this record */
+            mod_logutils[i]->log_print_record(rec_p, ref->rec.name,
+                mnt_pt, fs_type);
 
-    memset(fdata, 0, sizeof(*fdata));
-
-    HASH_ITER(hlink, file_hash, curr, tmp)
-    {
-        int64_t max;
-        int64_t r;
-        int64_t w;
-
-        max = max3(curr->counters[CP_SIZE_AT_OPEN],
-                   curr->counters[CP_MAX_BYTE_READ],
-                   curr->counters[CP_MAX_BYTE_WRITTEN]);
-
-        r = (curr->counters[CP_POSIX_READS]+
-             curr->counters[CP_POSIX_FREADS]+
-             curr->counters[CP_INDEP_READS]+
-             curr->counters[CP_COLL_READS]+
-             curr->counters[CP_SPLIT_READS]+
-             curr->counters[CP_NB_READS]);
-
-        w = (curr->counters[CP_POSIX_WRITES]+
-             curr->counters[CP_POSIX_FWRITES]+
-             curr->counters[CP_INDEP_WRITES]+
-             curr->counters[CP_COLL_WRITES]+
-             curr->counters[CP_SPLIT_WRITES]+
-             curr->counters[CP_NB_WRITES]);
-
-        fdata->total += 1;
-        fdata->total_size += max;
-        fdata->total_max = max(fdata->total_max, max);
-
-        if (r && !w)
-        {
-            fdata->read_only += 1;
-            fdata->read_only_size += max;
-            fdata->read_only_max = max(fdata->read_only_max, max);
-        }
-
-        if (!r && w)
-        {
-            fdata->write_only += 1;
-            fdata->write_only_size += max;
-            fdata->write_only_max = max(fdata->write_only_max, max);
-        }
-
-        if (r && w)
-        {
-            fdata->read_write += 1;
-            fdata->read_write_size += max;
-            fdata->read_write_max = max(fdata->read_write_max, max);
+            /* NOTE: we have to free the given file record data */
+            free(rec_p);
         }
 
-        if ((curr->type & (FILETYPE_SHARED|FILETYPE_PARTSHARED)))
-        {
-            fdata->shared += 1;
-            fdata->shared_size += max;
-            fdata->shared_max = max(fdata->shared_max, max);
-        }
-
-        if ((curr->type & (FILETYPE_UNIQUE)))
-        {
-            fdata->unique += 1;
-            fdata->unique_size += max;
-            fdata->unique_max = max(fdata->unique_max, max);
-        }
-    }
-
-    return;
-}
-
-void accum_perf(struct darshan_file *dfile,
-                hash_entry_t *hfile,
-                perf_data_t *pdata)
-{
-    int64_t mpi_file;
-
-    pdata->total_bytes += dfile->counters[CP_BYTES_READ] +
-                          dfile->counters[CP_BYTES_WRITTEN];
-
-    mpi_file = dfile->counters[CP_INDEP_OPENS] +
-               dfile->counters[CP_COLL_OPENS];
-
-    /*
-     * Calculation of Shared File Time
-     *   Four Methods!!!!
-     *     by_cumul: sum time counters and divide by nprocs
-     *               (inaccurate if lots of variance between procs)
-     *     by_open: difference between timestamp of open and close
-     *              (inaccurate if file is left open without i/o happening)
-     *     by_open_lastio: difference between timestamp of open and the
-     *                     timestamp of last i/o
-     *                     (similar to above but fixes case where file is left
-     *                      open after io is complete)
-     *     by_slowest: use slowest rank time from log data
-     *                 (most accurate but requires newer log version)
-     */
-    if (dfile->rank == -1)
-    {
-        /* by_open (same for MPI or POSIX) */
-        if (dfile->fcounters[CP_F_CLOSE_TIMESTAMP] >
-            dfile->fcounters[CP_F_OPEN_TIMESTAMP])
-        {
-            pdata->shared_time_by_open +=
-                dfile->fcounters[CP_F_CLOSE_TIMESTAMP] -
-                dfile->fcounters[CP_F_OPEN_TIMESTAMP];
-        }
-
-        /* by_open_lastio (same for MPI or POSIX) */
-        if (dfile->fcounters[CP_F_READ_END_TIMESTAMP] >
-            dfile->fcounters[CP_F_WRITE_END_TIMESTAMP])
-        {
-            /* be careful: file may have been opened but not read or written */
-            if(dfile->fcounters[CP_F_READ_END_TIMESTAMP] > dfile->fcounters[CP_F_OPEN_TIMESTAMP])
-            {
-                pdata->shared_time_by_open_lastio += 
-                    dfile->fcounters[CP_F_READ_END_TIMESTAMP] - 
-                    dfile->fcounters[CP_F_OPEN_TIMESTAMP];
-            }
-        }
-        else
+        if(ret < 0)
         {
-            /* be careful: file may have been opened but not read or written */
-            if(dfile->fcounters[CP_F_WRITE_END_TIMESTAMP] > dfile->fcounters[CP_F_OPEN_TIMESTAMP])
-            {
-                pdata->shared_time_by_open_lastio += 
-                    dfile->fcounters[CP_F_WRITE_END_TIMESTAMP] - 
-                    dfile->fcounters[CP_F_OPEN_TIMESTAMP];
-            }
-        }
-
-        /* by_cumul */
-        if (mpi_file)
-        {
-            pdata->shared_time_by_cumul +=
-                dfile->fcounters[CP_F_MPI_META_TIME] +
-                dfile->fcounters[CP_F_MPI_READ_TIME] +
-                dfile->fcounters[CP_F_MPI_WRITE_TIME];
-            pdata->shared_meta_time += dfile->fcounters[CP_F_MPI_META_TIME];
-        }
-        else
-        {
-            pdata->shared_time_by_cumul +=
-                dfile->fcounters[CP_F_POSIX_META_TIME] +
-                dfile->fcounters[CP_F_POSIX_READ_TIME] +
-                dfile->fcounters[CP_F_POSIX_WRITE_TIME];
-            pdata->shared_meta_time += dfile->fcounters[CP_F_POSIX_META_TIME];
+            fprintf(stderr, "Error: failed to parse module %s data\n",
+                darshan_module_names[i]);
+            fflush(stderr);
+            darshan_log_close(fd);
+            return(-1);
         }
-
-        /* by_slowest (same for MPI or POSIX) */
-        pdata->shared_time_by_slowest +=
-            dfile->fcounters[CP_F_SLOWEST_RANK_TIME];
     }
 
-    /*
-     * Calculation of Unique File Time
-     *   record the data for each file and sum it 
-     */
-    else
+    /* free mount info */
+    for(i=0; i<mount_count; i++)
     {
-        if (mpi_file)
-        {
-            pdata->rank_cumul_io_time[dfile->rank] += dfile->fcounters[CP_F_MPI_META_TIME] +
-                                dfile->fcounters[CP_F_MPI_READ_TIME] +
-                                dfile->fcounters[CP_F_MPI_WRITE_TIME];
-            pdata->rank_cumul_md_time[dfile->rank] += dfile->fcounters[CP_F_MPI_META_TIME];
-        }
-        else
-        {
-            pdata->rank_cumul_io_time[dfile->rank] += dfile->fcounters[CP_F_POSIX_META_TIME] +
-                                dfile->fcounters[CP_F_POSIX_READ_TIME] +
-                                dfile->fcounters[CP_F_POSIX_WRITE_TIME];
-            pdata->rank_cumul_md_time[dfile->rank] += dfile->fcounters[CP_F_POSIX_META_TIME];
-
-        }
+        free(mnt_pts[i]);
+        free(fs_types[i]);
     }
-
-    return;
-}
-
-void calc_perf(struct darshan_job *djob,
-               hash_entry_t *hash_rank_uniq,
-               perf_data_t *pdata)
-{
-    int64_t i;
-
-    pdata->shared_time_by_cumul =
-        pdata->shared_time_by_cumul / (double)djob->nprocs;
-
-    pdata->shared_meta_time = pdata->shared_meta_time / (double)djob->nprocs;
-
-    for (i=0; i<djob->nprocs; i++)
+    if(mount_count > 0)
     {
-        if (pdata->rank_cumul_io_time[i] > pdata->slowest_rank_time)
-        {
-            pdata->slowest_rank_time = pdata->rank_cumul_io_time[i];
-            pdata->slowest_rank_meta_time = pdata->rank_cumul_md_time[i];
-            pdata->slowest_rank_rank = i;
-        }
+        free(mnt_pts);
+        free(fs_types);
     }
 
-    if (pdata->slowest_rank_time + pdata->shared_time_by_cumul)
-    pdata->agg_perf_by_cumul = ((double)pdata->total_bytes / 1048576.0) /
-                                  (pdata->slowest_rank_time +
-                                   pdata->shared_time_by_cumul);
+    darshan_log_close(fd);
 
-    if (pdata->slowest_rank_time + pdata->shared_time_by_open)
-    pdata->agg_perf_by_open  = ((double)pdata->total_bytes / 1048576.0) / 
-                                   (pdata->slowest_rank_time +
-                                    pdata->shared_time_by_open);
-
-    if (pdata->slowest_rank_time + pdata->shared_time_by_open_lastio)
-    pdata->agg_perf_by_open_lastio = ((double)pdata->total_bytes / 1048576.0) /
-                                     (pdata->slowest_rank_time +
-                                      pdata->shared_time_by_open_lastio);
-
-    if (pdata->slowest_rank_time + pdata->shared_time_by_slowest)
-    pdata->agg_perf_by_slowest = ((double)pdata->total_bytes / 1048576.0) /
-                                     (pdata->slowest_rank_time +
-                                      pdata->slowest_rank_meta_time +
-                                      pdata->shared_time_by_slowest);
-
-    return;
+    return(0);
 }
diff --git a/darshan-util/darshan-posix-logutils.c b/darshan-util/darshan-posix-logutils.c
index 279a5ba..eedc4b5 100644
--- a/darshan-util/darshan-posix-logutils.c
+++ b/darshan-util/darshan-posix-logutils.c
@@ -30,14 +30,22 @@ char *posix_f_counter_names[] = {
 };
 #undef X
 
-int darshan_log_get_posix_file(darshan_fd fd, struct darshan_posix_file *file)
+struct darshan_mod_logutil_funcs posix_logutils =
+{
+    .log_get_record = &darshan_log_get_posix_file,
+    .log_print_record = &darshan_log_print_posix_file,
+};
+
+int darshan_log_get_posix_file(darshan_fd fd, void **file_rec,
+    darshan_record_id *rec_id)
 {
     int i;
     int ret;
+    struct darshan_posix_file *file = NULL;
 
-    /* reset file record, so that diff compares against a zero'd out record
-     * if file is missing
-     */
+    file = malloc(sizeof(*file));
+    if(!file)
+        return(-1);
     memset(file, 0, sizeof(*file));
 
     ret = darshan_log_get_moddat(fd, DARSHAN_POSIX_MOD,
@@ -56,9 +64,36 @@ int darshan_log_get_posix_file(darshan_fd fd, struct darshan_posix_file *file)
         }
     }
 
+    /* pass the file record back */
+    *file_rec = (void *)file;
+    *rec_id = file->f_id;
     return(ret);
 }
 
+void darshan_log_print_posix_file(void *file_rec, char *file_name,
+    char *mnt_pt, char *fs_type)
+{
+    int i;
+    struct darshan_posix_file *posix_file_rec =
+        (struct darshan_posix_file *)file_rec;
+
+    for(i=0; i<POSIX_NUM_INDICES; i++)
+    {
+        DARSHAN_COUNTER_PRINT(darshan_module_names[DARSHAN_POSIX_MOD],
+            posix_file_rec->rank, posix_file_rec->f_id, posix_counter_names[i],
+            posix_file_rec->counters[i], file_name, mnt_pt, fs_type);
+    }
+
+    for(i=0; i<POSIX_F_NUM_INDICES; i++)
+    {
+        DARSHAN_F_COUNTER_PRINT(darshan_module_names[DARSHAN_POSIX_MOD],
+            posix_file_rec->rank, posix_file_rec->f_id, posix_f_counter_names[i],
+            posix_file_rec->fcounters[i], file_name, mnt_pt, fs_type);
+    }
+
+    return;
+}
+
 /*
  * Local variables:
  *  c-indent-level: 4
diff --git a/darshan-util/darshan-posix-logutils.h b/darshan-util/darshan-posix-logutils.h
index 345e79e..b94060a 100644
--- a/darshan-util/darshan-posix-logutils.h
+++ b/darshan-util/darshan-posix-logutils.h
@@ -13,18 +13,11 @@
 extern char *posix_counter_names[];
 extern char *posix_f_counter_names[];
 
-int darshan_log_get_posix_file(darshan_fd fd, struct darshan_posix_file *file);
+extern struct darshan_mod_logutil_funcs posix_logutils;
 
-#define POSIX_COUNTER_PRINT(__file_rec, __counter) do { \
-    printf("%" PRId64 "\t%" PRIu64 "\t%s\t%" PRId64 "\n", \
-        (__file_rec)->rank, (__file_rec)->f_id, posix_counter_names[__counter], \
-        (__file_rec)->counters[__counter]); \
-} while(0)
-
-#define POSIX_F_COUNTER_PRINT(__file_rec, __counter) do { \
-    printf("%" PRId64 "\t%" PRIu64 "\t%s\t%f\n", \
-        (__file_rec)->rank, (__file_rec)->f_id, posix_f_counter_names[__counter], \
-        (__file_rec)->fcounters[__counter]); \
-} while(0)
+int darshan_log_get_posix_file(darshan_fd fd, void **file_rec,
+    darshan_record_id *rec_id);
+void darshan_log_print_posix_file(void *file_rec, char *file_name,
+    char *mnt_pt, char *fs_type);
 
 #endif
diff --git a/darshan-util/darshan-posix-parser.c b/darshan-util/darshan-posix-parser.c
deleted file mode 100644
index 6a1100f..0000000
--- a/darshan-util/darshan-posix-parser.c
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2015 University of Chicago.
- * See COPYRIGHT notice in top-level directory.
- *
- */
-
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <string.h>
-#include <zlib.h>
-#include <time.h>
-#include <stdlib.h>
-#include <getopt.h>
-#include <assert.h>
-
-#include "darshan-logutils.h"
-#include "darshan-posix-logutils.h"
-#include "uthash-1.9.2/src/uthash.h"
-
-int main(int argc, char **argv)
-{
-    int ret;
-    int i;
-    char *filename;
-    char tmp_string[4096];
-    darshan_fd fd;
-    struct darshan_header header;
-    struct darshan_job job;
-    struct darshan_record_ref *rec_hash = NULL;
-    struct darshan_record_ref *ref;
-    int mount_count;
-    char** mnt_pts;
-    char** fs_types;
-    time_t tmp_time = 0;
-    char *token;
-    char *save;
-    char buffer[DARSHAN_JOB_METADATA_LEN];
-    struct darshan_posix_file next_file;
-
-    assert(argc == 2);
-    filename = argv[1];
-
-    struct stat sbuf;
-    stat(filename, &sbuf);
-
-    fd = darshan_log_open(filename, "r");
-    if(!fd)
-    {
-        fprintf(stderr, "darshan_log_open() failed to open %s\n.", filename);
-        return(-1);
-    }
-
-    /* read darshan log header */
-    ret = darshan_log_getheader(fd, &header);
-    if(ret < 0)
-    {
-        fprintf(stderr, "darshan_log_getheader() failed to read log header.\n");
-        darshan_log_close(fd);
-        return(-1);
-    }
-
-    /* read darshan job info */
-    ret = darshan_log_getjob(fd, &job);
-    if(ret < 0)
-    {
-        fprintf(stderr, "darshan_log_getjob() failed to read job data.\n");
-        darshan_log_close(fd);
-        return(-1);
-    }
-
-    /* get the original command line for this job */
-    ret = darshan_log_getexe(fd, tmp_string);
-    if(ret < 0)
-    {
-        fprintf(stderr, "Error: unable to read trailing job information.\n");
-        darshan_log_close(fd);
-        return(-1);
-    }
-
-    /* print job summary */
-    printf("# darshan log version: %s\n", header.version_string);
-    printf("# size of POSIX file statistics: %zu bytes\n", sizeof(struct darshan_posix_file));
-    printf("# size of job statistics: %zu bytes\n", sizeof(struct darshan_job));
-    printf("# exe: %s\n", tmp_string);
-    printf("# uid: %" PRId64 "\n", job.uid);
-    printf("# jobid: %" PRId64 "\n", job.jobid);
-    printf("# start_time: %" PRId64 "\n", job.start_time);
-    tmp_time += job.start_time;
-    printf("# start_time_asci: %s", ctime(&tmp_time));
-    printf("# end_time: %" PRId64 "\n", job.end_time);
-    tmp_time = 0;
-    tmp_time += job.end_time;
-    printf("# end_time_asci: %s", ctime(&tmp_time));
-    printf("# nprocs: %" PRId64 "\n", job.nprocs);
-    printf("# run time: %" PRId64 "\n", job.end_time - job.start_time + 1);
-    for(token=strtok_r(job.metadata, "\n", &save);
-        token != NULL;
-        token=strtok_r(NULL, "\n", &save))
-    {
-        char *key;
-        char *value;
-        /* NOTE: we intentionally only split on the first = character.
-         * There may be additional = characters in the value portion
-         * (for example, when storing mpi-io hints).
-         */
-        strcpy(buffer, token);
-        key = buffer;
-        value = index(buffer, '=');
-        if(!value)
-            continue;
-        /* convert = to a null terminator to split key and value */
-        value[0] = '\0';
-        value++;
-        printf("# metadata: %s = %s\n", key, value);
-    }
-
-    /* get the mount information for this log */
-    ret = darshan_log_getmounts(fd, &mnt_pts, &fs_types, &mount_count);
-    if(ret < 0)
-    {
-        fprintf(stderr, "darshan_log_getmounts() failed to read mount information.\n");
-        darshan_log_close(fd);
-        return(-1);
-    }
-
-    /* print table of mounted file systems */
-    printf("\n# mounted file systems (mount point and fs type)\n");
-    printf("# -------------------------------------------------------\n");
-    for(i=0; i<mount_count; i++)
-    {
-        printf("# mount entry:\t%s\t%s\n", mnt_pts[i], fs_types[i]);
-    }
-
-    /* read hash of darshan records */
-    ret = darshan_log_gethash(fd, &rec_hash);
-    if(ret < 0)
-    {
-        fprintf(stderr, "darshan_log_getmap() failed to read record map.\n");
-        darshan_log_close(fd);
-        return(-1);
-    }
-
-    printf("\n*** FILE RECORD DATA ***\n");
- 
-    ret = darshan_log_get_posix_file(fd, &next_file);
-    if(ret < 0)
-    {
-        fprintf(stderr, "darshan_log_get_posix_file() failed to read next record.\n");
-        darshan_log_close(fd);
-        return(-1);
-    }
-    if(ret == 0)
-    {
-        printf("# no files opened.\n");
-        darshan_log_close(fd);
-        return(0);
-    }
-   
-    /* loop over each stored POSIX file record and print counters */
-    do
-    {
-        /* get the pathname for this record */
-        HASH_FIND(hlink, rec_hash, &next_file.f_id, sizeof(darshan_record_id), ref);
-        assert(ref);
-
-        for(i=0; i<POSIX_NUM_INDICES; i++)
-        {
-            POSIX_COUNTER_PRINT(&next_file, i);
-        }
-        for(i=0; i<POSIX_F_NUM_INDICES; i++)
-        {
-            POSIX_F_COUNTER_PRINT(&next_file, i);
-        }
-
-    } while((ret = darshan_log_get_posix_file(fd, &next_file)) == 1);
-
-    /* free mount info */
-    for(i=0; i<mount_count; i++)
-    {
-        free(mnt_pts[i]);
-        free(fs_types[i]);
-    }
-    if(mount_count > 0)
-    {
-        free(mnt_pts);
-        free(fs_types);
-    }
-
-    darshan_log_close(fd);
-
-    return(0);
-}


hooks/post-receive
--



More information about the Darshan-commits mailing list