darshan-core.c 53.2 KB
Newer Older
1
/*
Shane Snyder's avatar
Shane Snyder committed
2 3 4
 * Copyright (C) 2015 University of Chicago.
 * See COPYRIGHT notice in top-level directory.
 *
5 6
 */

7
#define _XOPEN_SOURCE 500
8
#define _GNU_SOURCE
9

10 11 12 13 14 15
#include "darshan-runtime-config.h"

#include <stdio.h>
#ifdef HAVE_MNTENT_H
#include <mntent.h>
#endif
16 17 18 19 20
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <limits.h>
#include <pthread.h>
21
#include <fcntl.h>
22 23
#include <sys/types.h>
#include <sys/stat.h>
24
#include <sys/mman.h>
25
#include <sys/vfs.h>
26
#include <zlib.h>
27
#include <mpi.h>
28
#include <assert.h>
29

30
#include "uthash.h"
Shane Snyder's avatar
Shane Snyder committed
31
#include "darshan.h"
32
#include "darshan-core.h"
Shane Snyder's avatar
Shane Snyder committed
33
#include "darshan-dynamic.h"
34

35
extern char* __progname;
36
extern char* __progname_full;
37

38
/* internal variable delcarations */
39
static struct darshan_core_runtime *darshan_core = NULL;
40
static pthread_mutex_t darshan_core_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
41
static int my_rank = -1;
42
static int nprocs = -1;
43
static int darshan_mem_alignment = 1;
44

45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
/* paths prefixed with the following directories are not traced by darshan */
char* darshan_path_exclusions[] = {
"/etc/",
"/dev/",
"/usr/",
"/bin/",
"/boot/",
"/lib/",
"/opt/",
"/sbin/",
"/sys/",
"/proc/",
NULL
};

60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
#ifdef DARSHAN_BGQ
extern void bgq_runtime_initialize();
#endif

/* array of init functions for modules which need to be statically
 * initialized by darshan at startup time
 */
void (*mod_static_init_fns[])(void) =
{
#ifdef DARSHAN_BGQ
    &bgq_runtime_initialize,
#endif
    NULL
};

Shane Snyder's avatar
Shane Snyder committed
75 76 77
#define DARSHAN_CORE_LOCK() pthread_mutex_lock(&darshan_core_mutex)
#define DARSHAN_CORE_UNLOCK() pthread_mutex_unlock(&darshan_core_mutex)

78 79 80 81 82 83
/* FS mount information */
#define DARSHAN_MAX_MNTS 64
#define DARSHAN_MAX_MNT_PATH 256
#define DARSHAN_MAX_MNT_TYPE 32
struct mnt_data
{
84
    int block_size;
85 86 87 88 89 90
    char path[DARSHAN_MAX_MNT_PATH];
    char type[DARSHAN_MAX_MNT_TYPE];
};
static struct mnt_data mnt_data_array[DARSHAN_MAX_MNTS];
static int mnt_data_count = 0;

91 92 93 94
/* prototypes for internal helper functions */
static void darshan_get_logfile_name(
    char* logfile_name, int jobid, struct tm* start_tm);
static void darshan_log_record_hints_and_ver(
95 96
    struct darshan_core_runtime* core);
static void darshan_get_exe_and_mounts_root(
97 98 99
    struct darshan_core_runtime *core, int argc, char **argv);
static void darshan_get_exe_and_mounts(
    struct darshan_core_runtime *core, int argc, char **argv);
100 101
static void darshan_block_size_from_path(
    const char *path, int *block_size);
102
static void darshan_get_shared_records(
103
    struct darshan_core_runtime *core, darshan_record_id *shared_recs);
104
static int darshan_log_open_all(
105
    char *logfile_name, MPI_File *log_fh);
106
static int darshan_deflate_buffer(
Shane Snyder's avatar
Shane Snyder committed
107 108
    void **pointers, int *lengths, int count, char *comp_buf,
    int *comp_buf_length);
109
static int darshan_log_write_record_hash(
110
    MPI_File log_fh, struct darshan_core_runtime *core,
111 112 113
    uint64_t *inout_off);
static int darshan_log_append_all(
    MPI_File log_fh, struct darshan_core_runtime *core, void *buf,
Shane Snyder's avatar
Shane Snyder committed
114
    int count, uint64_t *inout_off);
Shane Snyder's avatar
Shane Snyder committed
115 116
static void darshan_core_cleanup(
    struct darshan_core_runtime* core);
117

118 119
/* *********************************** */

Shane Snyder's avatar
Shane Snyder committed
120
void darshan_core_initialize(int argc, char **argv)
121
{
122
    struct darshan_core_runtime *init_core = NULL;
123 124
    int internal_timing_flag = 0;
    double init_start, init_time, init_max;
125 126 127 128
    char *mmap_log_name = "darshan-log.out";
    int mmap_fd;
    int mmap_size;
    int sys_page_size;
129
    char *envstr;
130 131
    char *jobid_str;
    int jobid;
132 133
    int ret;
    int tmpval;
134
    int i;
135 136

    DARSHAN_MPI_CALL(PMPI_Comm_size)(MPI_COMM_WORLD, &nprocs);
137
    DARSHAN_MPI_CALL(PMPI_Comm_rank)(MPI_COMM_WORLD, &my_rank);
138 139 140 141

    if(getenv("DARSHAN_INTERNAL_TIMING"))
        internal_timing_flag = 1;

142
    if(internal_timing_flag)
143 144 145
        init_start = DARSHAN_MPI_CALL(PMPI_Wtime)();

    /* setup darshan runtime if darshan is enabled and hasn't been initialized already */
146
    if(!getenv("DARSHAN_DISABLE") && !darshan_core)
147
    {
148
        #if (__DARSHAN_MEM_ALIGNMENT < 1)
149 150
            #error Darshan must be configured with a positive value for --with-mem-align
        #endif
151
        envstr = getenv(DARSHAN_MEM_ALIGNMENT_OVERRIDE);
152 153 154 155 156 157 158 159 160 161 162
        if(envstr)
        {
            ret = sscanf(envstr, "%d", &tmpval);
            /* silently ignore if the env variable is set poorly */
            if(ret == 1 && tmpval > 0)
            {
                darshan_mem_alignment = tmpval;
            }
        }
        else
        {
163
            darshan_mem_alignment = __DARSHAN_MEM_ALIGNMENT;
164 165 166 167 168 169 170
        }

        /* avoid floating point errors on faulty input */
        if (darshan_mem_alignment < 1)
        {
            darshan_mem_alignment = 1;
        }
171

172 173 174
        /* allocate structure to track darshan core runtime information */
        init_core = malloc(sizeof(*init_core));
        if(init_core)
175
        {
176 177 178 179 180
            memset(init_core, 0, sizeof(*init_core));
            init_core->wtime_offset = DARSHAN_MPI_CALL(PMPI_Wtime)();

            sys_page_size = sysconf(_SC_PAGESIZE);
            assert(sys_page_size > 0);
181

182 183 184 185 186 187 188
            /* set the size of the mmap, making sure to round up to the
             * nearest page size. One mmap chunk is used for the job-level
             * metadata, and the rest are statically assigned to modules
             */
            mmap_size = (1 + DARSHAN_MAX_MODS) * DARSHAN_MMAP_CHUNK_SIZE;
            if(mmap_size % sys_page_size)
                mmap_size = ((mmap_size / sys_page_size) + 1) * sys_page_size;
189

190 191 192
            /* TODO: logfile name should have process rank in it for uniqueness */
            mmap_fd = open(mmap_log_name, O_CREAT|O_RDWR|O_EXCL , 0644);
            if(mmap_fd < 0)
193
            {
194 195 196 197
                fprintf(stderr, "darshan library warning: "
                    "unable to create darshan log file %s\n", mmap_log_name);
                free(init_core);
                return;
198 199
            }

200 201 202 203 204 205 206 207 208 209 210 211 212 213
            /* allocate the necessary space in the log file */
            ret = ftruncate(mmap_fd, mmap_size);
            if(ret < 0)
            {
                fprintf(stderr, "darshan library warning: "
                    "unable to allocate darshan log file %s\n", mmap_log_name);
                free(init_core);
                close(mmap_fd);
                unlink(mmap_log_name);
                return;
            }

            /* memory map buffers for getting at least some summary i/o data
             * into a log file if darshan does not shut down properly
214
             */
215 216 217
            init_core->mmap_p = mmap(NULL, mmap_size, PROT_WRITE, MAP_SHARED,
                mmap_fd, 0);
            if(init_core->mmap_p == MAP_FAILED)
218
            {
219 220 221 222 223 224
                fprintf(stderr, "darshan library warning: "
                    "unable to mmap darshan log file %s\n", mmap_log_name);
                free(init_core);
                close(mmap_fd);
                unlink(mmap_log_name);
                return;
225 226
            }

227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
            /* close darshan log file (this does *not* unmap the log file) */
            close(mmap_fd);

            /* set the pointers for each log file region */
            init_core->mmap_job_p = (struct darshan_job *)(init_core->mmap_p);
            init_core->mmap_exe_mnt_p =
                (char *)(((char *)init_core->mmap_p) + sizeof(struct darshan_job));
            init_core->mmap_mod_p =
                (void *)(((char *)init_core->mmap_p) + DARSHAN_MMAP_CHUNK_SIZE);

            /* set known job-level metadata files for the log file */
            init_core->mmap_job_p->uid = getuid();
            init_core->mmap_job_p->start_time = time(NULL);
            init_core->mmap_job_p->nprocs = nprocs;

            /* Use DARSHAN_JOBID_OVERRIDE for the env var for __DARSHAN_JOBID */
            envstr = getenv(DARSHAN_JOBID_OVERRIDE);
            if(!envstr)
245
            {
246
                envstr = __DARSHAN_JOBID;
247
            }
248

249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
            /* find a job id */
            jobid_str = getenv(envstr);
            if(jobid_str)
            {
                /* in cobalt we can find it in env var */
                ret = sscanf(jobid_str, "%d", &jobid);
            }
            if(!jobid_str || ret != 1)
            {
                /* use pid as fall back */
                jobid = getpid();
            }
            init_core->mmap_job_p->jobid = (int64_t)jobid;

            /* if we are using any hints to write the log file, then record those
             * hints with the darshan job information
             */
            darshan_log_record_hints_and_ver(init_core);

268
            /* collect information about command line and mounted file systems */
269
            darshan_get_exe_and_mounts(init_core, argc, argv);
270

271 272 273 274 275 276 277 278 279 280 281
            /* TODO: what would be needed in a termination routine? set job end time? */

            /* maybe bootstrap modules with static initializers */
            i = 0;
            while(mod_static_init_fns[i])
            {
                (*mod_static_init_fns[i])();
                i++;
            }

            darshan_core = init_core;
282
        }
283 284
    }

285 286 287 288 289
    if(internal_timing_flag)
    {
        init_time = DARSHAN_MPI_CALL(PMPI_Wtime)() - init_start;
        DARSHAN_MPI_CALL(PMPI_Reduce)(&init_time, &init_max, 1,
            MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD);
290
        if(my_rank == 0)
291
        {
292 293
            fprintf(stderr, "#darshan:<op>\t<nprocs>\t<time>\n");
            fprintf(stderr, "darshan:init\t%d\t%f\n", nprocs, init_max);
294 295 296 297 298 299
        }
    }

    return;
}

Shane Snyder's avatar
Shane Snyder committed
300
void darshan_core_shutdown()
301
{
302 303 304

    return;
#if 0
305
    int i;
306
    char *logfile_name;
307
    struct darshan_core_runtime *final_core;
308
    int internal_timing_flag = 0;
309 310
    char *envjobid;
    char *jobid_str;
311
    int jobid;
312
    struct tm *start_tm;
313
    time_t start_time_tmp;
314 315
    int ret = 0;
    int all_ret = 0;
316 317
    int64_t first_start_time;
    int64_t last_end_time;
318 319
    int local_mod_use[DARSHAN_MAX_MODS] = {0};
    int global_mod_use_count[DARSHAN_MAX_MODS] = {0};
320
    darshan_record_id shared_recs[DARSHAN_CORE_MAX_RECORDS] = {0};
321
    double start_log_time;
322 323 324 325 326 327 328
    double open1, open2;
    double job1, job2;
    double rec1, rec2;
    double mod1[DARSHAN_MAX_MODS] = {0};
    double mod2[DARSHAN_MAX_MODS] = {0};
    double header1, header2;
    double tm_end;
329
    uint64_t gz_fp = 0;
330
    unsigned char tmp_partial_flag;
331 332
    MPI_File log_fh;
    MPI_Status status;
333 334 335 336

    if(getenv("DARSHAN_INTERNAL_TIMING"))
        internal_timing_flag = 1;

337 338
    start_log_time = DARSHAN_MPI_CALL(PMPI_Wtime)();

Shane Snyder's avatar
Shane Snyder committed
339
    /* disable darhan-core while we shutdown */
340
    DARSHAN_CORE_LOCK();
341
    if(!darshan_core)
342
    {
343
        DARSHAN_CORE_UNLOCK();
344 345
        return;
    }
346 347
    final_core = darshan_core;
    darshan_core = NULL;
Shane Snyder's avatar
Shane Snyder committed
348

349
    /* we also need to set which modules were registered on this process and
350 351
     * call into those modules and give them a chance to perform any necessary
     * pre-shutdown steps.
Shane Snyder's avatar
Shane Snyder committed
352 353 354 355 356 357
     */
    for(i = 0; i < DARSHAN_MAX_MODS; i++)
    {
        if(final_core->mod_array[i])
        {
            local_mod_use[i] = 1;
358
            final_core->mod_array[i]->mod_funcs.begin_shutdown();
Shane Snyder's avatar
Shane Snyder committed
359 360
        }
    }
361
    DARSHAN_CORE_UNLOCK();
362 363 364 365

    logfile_name = malloc(PATH_MAX);
    if(!logfile_name)
    {
366
        darshan_core_cleanup(final_core);
367 368 369
        return;
    }

370
    /* set darshan job id/metadata and constuct log file name on rank 0 */
371
    if(my_rank == 0)
372
    {
373
        /* Use DARSHAN_JOBID_OVERRIDE for the env var for __DARSHAN_JOBID */
374
        envjobid = getenv(DARSHAN_JOBID_OVERRIDE);
375
        if(!envjobid)
376
        {
377
            envjobid = __DARSHAN_JOBID;
378 379
        }

380
        /* find a job id */
381 382 383 384 385 386 387 388 389 390 391 392
        jobid_str = getenv(envjobid);
        if(jobid_str)
        {
            /* in cobalt we can find it in env var */
            ret = sscanf(jobid_str, "%d", &jobid);
        }
        if(!jobid_str || ret != 1)
        {
            /* use pid as fall back */
            jobid = getpid();
        }

393
        final_core->log_job.jobid = (int64_t)jobid;
394

395
        /* if we are using any hints to write the log file, then record those
396
         * hints with the darshan job information
397
         */
398
        darshan_log_record_hints_and_ver(final_core);
399

400
        /* use human readable start time format in log filename */
401
        start_time_tmp = final_core->log_job.start_time;
402
        start_tm = localtime(&start_time_tmp);
403

404 405
        /* construct log file name */
        darshan_get_logfile_name(logfile_name, jobid, start_tm);
406 407 408 409 410 411 412 413 414
    }

    /* broadcast log file name */
    DARSHAN_MPI_CALL(PMPI_Bcast)(logfile_name, PATH_MAX, MPI_CHAR, 0,
        MPI_COMM_WORLD);

    if(strlen(logfile_name) == 0)
    {
        /* failed to generate log file name */
415
        free(logfile_name);
416
        darshan_core_cleanup(final_core);
417 418 419
        return;
    }

420
    final_core->log_job.end_time = time(NULL);
421

422 423 424
    /* reduce to report first start time and last end time across all ranks
     * at rank 0
     */
425 426
    DARSHAN_MPI_CALL(PMPI_Reduce)(&final_core->log_job.start_time, &first_start_time, 1, MPI_LONG_LONG, MPI_MIN, 0, MPI_COMM_WORLD);
    DARSHAN_MPI_CALL(PMPI_Reduce)(&final_core->log_job.end_time, &last_end_time, 1, MPI_LONG_LONG, MPI_MAX, 0, MPI_COMM_WORLD);
427 428
    if(my_rank == 0)
    {
429 430
        final_core->log_job.start_time = first_start_time;
        final_core->log_job.end_time = last_end_time;
431
    }
432

433 434 435
    /* reduce the number of times a module was opened globally and bcast to everyone */   
    DARSHAN_MPI_CALL(PMPI_Allreduce)(local_mod_use, global_mod_use_count, DARSHAN_MAX_MODS, MPI_INT, MPI_SUM, MPI_COMM_WORLD);

436
    /* get a list of records which are shared across all processes */
437
    darshan_get_shared_records(final_core, shared_recs);
438

439 440
    if(internal_timing_flag)
        open1 = DARSHAN_MPI_CALL(PMPI_Wtime)();
441
    /* collectively open the darshan log file */
442
    ret = darshan_log_open_all(logfile_name, &log_fh);
443 444
    if(internal_timing_flag)
        open2 = DARSHAN_MPI_CALL(PMPI_Wtime)();
445 446 447 448 449 450 451 452

    /* error out if unable to open log file */
    DARSHAN_MPI_CALL(PMPI_Allreduce)(&ret, &all_ret, 1, MPI_INT,
        MPI_LOR, MPI_COMM_WORLD);
    if(all_ret != 0)
    {
        if(my_rank == 0)
        {
453 454
            fprintf(stderr, "darshan library warning: unable to open log file %s\n",
                logfile_name);
455 456 457
            unlink(logfile_name);
        }
        free(logfile_name);
458
        darshan_core_cleanup(final_core);
459 460 461
        return;
    }

462 463
    if(internal_timing_flag)
        job1 = DARSHAN_MPI_CALL(PMPI_Wtime)();
464
    /* rank 0 is responsible for writing the compressed darshan job information */
Shane Snyder's avatar
Shane Snyder committed
465
    if(my_rank == 0)
466
    {
467
        void *pointers[2] = {&final_core->log_job, final_core->trailing_data};
468
        int lengths[2] = {sizeof(struct darshan_job), strlen(final_core->trailing_data)};
469
        int comp_buf_sz = 0;
470

471
        /* compress the job info and the trailing mount/exe data */
Shane Snyder's avatar
Shane Snyder committed
472
        all_ret = darshan_deflate_buffer(pointers, lengths, 2,
473 474
            final_core->comp_buf, &comp_buf_sz);
        if(all_ret)
475
        {
476
            fprintf(stderr, "darshan library warning: unable to compress job data\n");
477
            unlink(logfile_name);
478
        }
479 480 481
        else
        {
            /* write the job information, preallocing space for the log header */
Shane Snyder's avatar
Shane Snyder committed
482
            gz_fp += sizeof(struct darshan_header);
483 484
            all_ret = DARSHAN_MPI_CALL(PMPI_File_write_at)(log_fh, gz_fp,
                final_core->comp_buf, comp_buf_sz, MPI_BYTE, &status);
485 486 487 488 489
            if(all_ret != MPI_SUCCESS)
            {
                fprintf(stderr, "darshan library warning: unable to write job data to log file %s\n",
                        logfile_name);
                unlink(logfile_name);
Shane Snyder's avatar
Shane Snyder committed
490
                
491
            }
492
            gz_fp += comp_buf_sz;
493
        }
494 495
    }

496 497 498 499 500
    /* error out if unable to write job information */
    DARSHAN_MPI_CALL(PMPI_Bcast)(&all_ret, 1, MPI_INT, 0, MPI_COMM_WORLD);
    if(all_ret != 0)
    {
        free(logfile_name);
501
        darshan_core_cleanup(final_core);
502 503
        return;
    }
504 505
    if(internal_timing_flag)
        job2 = DARSHAN_MPI_CALL(PMPI_Wtime)();
506

507 508
    if(internal_timing_flag)
        rec1 = DARSHAN_MPI_CALL(PMPI_Wtime)();
509
    /* write the record name->id hash to the log file */
Shane Snyder's avatar
Shane Snyder committed
510
    final_core->log_header.rec_map.off = gz_fp;
511
    ret = darshan_log_write_record_hash(log_fh, final_core, &gz_fp);
Shane Snyder's avatar
Shane Snyder committed
512
    final_core->log_header.rec_map.len = gz_fp - final_core->log_header.rec_map.off;
513

514
    /* error out if unable to write record hash */
515 516 517 518 519 520
    DARSHAN_MPI_CALL(PMPI_Allreduce)(&ret, &all_ret, 1, MPI_INT,
        MPI_LOR, MPI_COMM_WORLD);
    if(all_ret != 0)
    {
        if(my_rank == 0)
        {
521
            fprintf(stderr, "darshan library warning: unable to write record hash to log file %s\n",
522
                logfile_name);
523
            unlink(logfile_name);
524 525
        }
        free(logfile_name);
526
        darshan_core_cleanup(final_core);
527 528
        return;
    }
Shane Snyder's avatar
Shane Snyder committed
529 530
    if(internal_timing_flag)
        rec2 = DARSHAN_MPI_CALL(PMPI_Wtime)();
531 532

    /* loop over globally used darshan modules and:
533
     *      - perform shared file reductions, if possible
534
     *      - get final output buffer
535
     *      - compress (zlib) provided output buffer
Shane Snyder's avatar
Shane Snyder committed
536
     *      - append compressed buffer to log file
537 538
     *      - add module index info (file offset/length) to log header
     *      - shutdown the module
539
     */
540
    for(i = 0; i < DARSHAN_MAX_MODS; i++)
541
    {
542
        struct darshan_core_module* this_mod = final_core->mod_array[i];
543
        struct darshan_core_record_ref *ref = NULL;
544 545
        darshan_record_id mod_shared_recs[DARSHAN_CORE_MAX_RECORDS];
        int mod_shared_rec_cnt = 0;
546
        void* mod_buf = NULL;
547
        int mod_buf_sz = 0;
548
        int j;
549

550
        if(global_mod_use_count[i] == 0)
551 552
        {
            if(my_rank == 0)
553 554 555 556
            {
                final_core->log_header.mod_map[i].off = 0;
                final_core->log_header.mod_map[i].len = 0;
            }
557
            continue;
558
        }
559 560
 
        if(internal_timing_flag)
561
            mod1[i] = DARSHAN_MPI_CALL(PMPI_Wtime)();
562

563 564 565 566 567 568 569 570
        /* set the shared file list for this module */
        memset(mod_shared_recs, 0, DARSHAN_CORE_MAX_RECORDS * sizeof(darshan_record_id));
        for(j = 0; j < DARSHAN_CORE_MAX_RECORDS && shared_recs[j] != 0; j++)
        {
            HASH_FIND(hlink, final_core->rec_hash, &shared_recs[j],
                sizeof(darshan_record_id), ref);
            assert(ref);
            if(DARSHAN_CORE_MOD_ISSET(ref->global_mod_flags, i))
571
            {
572
                mod_shared_recs[mod_shared_rec_cnt++] = shared_recs[j];
573
            }
574
        }
575

576 577 578 579 580
        /* if module is registered locally, get the corresponding output buffer
         * 
         * NOTE: this function can be used to run collective operations across
         * modules, if there are file records shared globally.
         */
581
        if(this_mod)
582
        {
583 584
            this_mod->mod_funcs.get_output_data(MPI_COMM_WORLD, mod_shared_recs,
                mod_shared_rec_cnt, &mod_buf, &mod_buf_sz);
585 586
        }

587
        /* append this module's data to the darshan log */
Shane Snyder's avatar
Shane Snyder committed
588 589 590 591
        final_core->log_header.mod_map[i].off = gz_fp;
        ret = darshan_log_append_all(log_fh, final_core, mod_buf, mod_buf_sz, &gz_fp);
        final_core->log_header.mod_map[i].len =
            gz_fp - final_core->log_header.mod_map[i].off;
592

593
        /* error out if the log append failed */
594 595 596
        DARSHAN_MPI_CALL(PMPI_Allreduce)(&ret, &all_ret, 1, MPI_INT,
            MPI_LOR, MPI_COMM_WORLD);
        if(all_ret != 0)
597
        {
598 599 600 601 602 603 604 605
            if(my_rank == 0)
            {
                fprintf(stderr,
                    "darshan library warning: unable to write %s module data to log file %s\n",
                    darshan_module_names[i], logfile_name);
                unlink(logfile_name);
            }
            free(logfile_name);
606
            darshan_core_cleanup(final_core);
607
            return;
608 609 610
        }

        /* shutdown module if registered locally */
611
        if(this_mod)
612 613 614
        {
            this_mod->mod_funcs.shutdown();
        }
615 616
        if(internal_timing_flag)
            mod2[i] = DARSHAN_MPI_CALL(PMPI_Wtime)();
617 618
    }

619 620 621 622 623 624 625
    /* run a reduction to determine if any application processes had to set the
     * partial flag. this happens when a process has tracked too many records
     * at once and cannot track new records
     */
    DARSHAN_MPI_CALL(PMPI_Reduce)(&(final_core->log_header.partial_flag),
        &tmp_partial_flag, 1, MPI_UNSIGNED_CHAR, MPI_MAX, 0, MPI_COMM_WORLD);

626 627
    if(internal_timing_flag)
        header1 = DARSHAN_MPI_CALL(PMPI_Wtime)();
628
    /* rank 0 is responsible for writing the log header */
629 630
    if(my_rank == 0)
    {
631 632 633
        /* initialize the remaining header fields */
        strcpy(final_core->log_header.version_string, DARSHAN_LOG_VERSION);
        final_core->log_header.magic_nr = DARSHAN_MAGIC_NR;
634
        final_core->log_header.comp_type = DARSHAN_ZLIB_COMP;
635
        final_core->log_header.partial_flag = tmp_partial_flag;
636

Shane Snyder's avatar
Shane Snyder committed
637 638 639
        all_ret = DARSHAN_MPI_CALL(PMPI_File_write_at)(log_fh, 0, &(final_core->log_header),
            sizeof(struct darshan_header), MPI_BYTE, &status);
        if(all_ret != MPI_SUCCESS)
640
        {
Shane Snyder's avatar
Shane Snyder committed
641 642
            fprintf(stderr, "darshan library warning: unable to write header to log file %s\n",
                    logfile_name);
643
            unlink(logfile_name);
644
        }
645 646
    }

647 648 649 650 651
    /* error out if unable to write log header */
    DARSHAN_MPI_CALL(PMPI_Bcast)(&all_ret, 1, MPI_INT, 0, MPI_COMM_WORLD);
    if(all_ret != 0)
    {
        free(logfile_name);
652
        darshan_core_cleanup(final_core);
653 654
        return;
    }
655 656
    if(internal_timing_flag)
        header2 = DARSHAN_MPI_CALL(PMPI_Wtime)();
657

658 659 660
    DARSHAN_MPI_CALL(PMPI_File_close)(&log_fh);

    /* if we got this far, there are no errors, so rename from *.darshan_partial
661
     * to *-<logwritetime>.darshan, which indicates that this log file is
662 663
     * complete and ready for analysis
     */
664 665
    if(my_rank == 0)
    {
Shane Snyder's avatar
Shane Snyder committed
666
        if(getenv("DARSHAN_LOGFILE"))
667
        {
668
#ifdef __DARSHAN_GROUP_READABLE_LOGS
Shane Snyder's avatar
Shane Snyder committed
669
            chmod(logfile_name, (S_IRUSR|S_IRGRP));
670
#else
Shane Snyder's avatar
Shane Snyder committed
671
            chmod(logfile_name, (S_IRUSR));
672
#endif
Shane Snyder's avatar
Shane Snyder committed
673 674 675 676 677 678 679 680 681 682 683 684 685 686
        }
        else
        {
            char* tmp_index;
            double end_log_time;
            char* new_logfile_name;

            new_logfile_name = malloc(PATH_MAX);
            if(new_logfile_name)
            {
                new_logfile_name[0] = '\0';
                end_log_time = DARSHAN_MPI_CALL(PMPI_Wtime)();
                strcat(new_logfile_name, logfile_name);
                tmp_index = strstr(new_logfile_name, ".darshan_partial");
687
                sprintf(tmp_index, "_%d.darshan", (int)(end_log_time-start_log_time+1));
Shane Snyder's avatar
Shane Snyder committed
688 689
                rename(logfile_name, new_logfile_name);
                /* set permissions on log file */
690
#ifdef __DARSHAN_GROUP_READABLE_LOGS
Shane Snyder's avatar
Shane Snyder committed
691 692 693 694 695 696
                chmod(new_logfile_name, (S_IRUSR|S_IRGRP));
#else
                chmod(new_logfile_name, (S_IRUSR));
#endif
                free(new_logfile_name);
            }
697
        }
698
    }
699

700
    free(logfile_name);
701
    darshan_core_cleanup(final_core);
702

703
    if(internal_timing_flag)
704
    {
705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738
        double open_tm, open_slowest;
        double header_tm, header_slowest;
        double job_tm, job_slowest;
        double rec_tm, rec_slowest;
        double mod_tm[DARSHAN_MAX_MODS], mod_slowest[DARSHAN_MAX_MODS];
        double all_tm, all_slowest;

        tm_end = DARSHAN_MPI_CALL(PMPI_Wtime)();

        open_tm = open2 - open1;
        header_tm = header2 - header1;
        job_tm = job2 - job1;
        rec_tm = rec2 - rec1;
        all_tm = tm_end - start_log_time;
        for(i = 0;i < DARSHAN_MAX_MODS; i++)
        {
            mod_tm[i] = mod2[i] - mod1[i];
        }

        DARSHAN_MPI_CALL(PMPI_Reduce)(&open_tm, &open_slowest, 1,
            MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD);
        DARSHAN_MPI_CALL(PMPI_Reduce)(&header_tm, &header_slowest, 1,
            MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD);
        DARSHAN_MPI_CALL(PMPI_Reduce)(&job_tm, &job_slowest, 1,
            MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD);
        DARSHAN_MPI_CALL(PMPI_Reduce)(&rec_tm, &rec_slowest, 1,
            MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD);
        DARSHAN_MPI_CALL(PMPI_Reduce)(&all_tm, &all_slowest, 1,
            MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD);
        DARSHAN_MPI_CALL(PMPI_Reduce)(mod_tm, mod_slowest, DARSHAN_MAX_MODS,
            MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD);

        if(my_rank == 0)
        {
739 740 741 742 743
            fprintf(stderr, "#darshan:<op>\t<nprocs>\t<time>\n");
            fprintf(stderr, "darshan:log_open\t%d\t%f\n", nprocs, open_slowest);
            fprintf(stderr, "darshan:job_write\t%d\t%f\n", nprocs, job_slowest);
            fprintf(stderr, "darshan:hash_write\t%d\t%f\n", nprocs, rec_slowest);
            fprintf(stderr, "darshan:header_write\t%d\t%f\n", nprocs, header_slowest);
744 745 746
            for(i = 0; i < DARSHAN_MAX_MODS; i++)
            {
                if(global_mod_use_count[i])
747
                    fprintf(stderr, "darshan:%s_shutdown\t%d\t%f\n", darshan_module_names[i],
Shane Snyder's avatar
Shane Snyder committed
748
                        nprocs, mod_slowest[i]);
749
            }
750
            fprintf(stderr, "darshan:core_shutdown\t%d\t%f\n", nprocs, all_slowest);
751
        }
752
    }
753

754
    return;
755
#endif
756
}
757

Shane Snyder's avatar
Shane Snyder committed
758
/* *********************************** */
759

760
/* construct the darshan log file name */
761
static void darshan_get_logfile_name(char* logfile_name, int jobid, struct tm* start_tm)
762
{
Shane Snyder's avatar
Shane Snyder committed
763
    char* user_logfile_name;
764 765 766
    char* logpath;
    char* logname_string;
    char* logpath_override = NULL;
767
#ifdef __DARSHAN_LOG_ENV
768 769 770 771 772 773 774 775 776
    char env_check[256];
    char* env_tok;
#endif
    uint64_t hlevel;
    char hname[HOST_NAME_MAX];
    uint64_t logmod;
    char cuser[L_cuserid] = {0};
    int ret;

Shane Snyder's avatar
Shane Snyder committed
777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
    /* first, check if user specifies a complete logpath to use */
    user_logfile_name = getenv("DARSHAN_LOGFILE");
    if(user_logfile_name)
    {
        if(strlen(user_logfile_name) >= (PATH_MAX-1))
        {
            fprintf(stderr, "darshan library warning: user log file name too long.\n");
            logfile_name[0] = '\0';
        }
        else
        {
            strcpy(logfile_name, user_logfile_name);
        }
    }
    else
792
    {
Shane Snyder's avatar
Shane Snyder committed
793 794
        /* otherwise, generate the log path automatically */

795 796
        /* Use DARSHAN_LOG_PATH_OVERRIDE for the value or __DARSHAN_LOG_PATH */
        logpath = getenv(DARSHAN_LOG_PATH_OVERRIDE);
Shane Snyder's avatar
Shane Snyder committed
797 798
        if(!logpath)
        {
799 800
#ifdef __DARSHAN_LOG_PATH
            logpath = __DARSHAN_LOG_PATH;
801
#endif
Shane Snyder's avatar
Shane Snyder committed
802
        }
803

Shane Snyder's avatar
Shane Snyder committed
804 805 806 807 808 809 810 811 812 813
        /* get the username for this job.  In order we will try each of the
         * following until one of them succeeds:
         *
         * - cuserid()
         * - getenv("LOGNAME")
         * - snprintf(..., geteuid());
         *
         * Note that we do not use getpwuid() because it generally will not
         * work in statically compiled binaries.
         */
814 815

#ifndef DARSHAN_DISABLE_CUSERID
Shane Snyder's avatar
Shane Snyder committed
816
        cuserid(cuser);
817 818
#endif

Shane Snyder's avatar
Shane Snyder committed
819 820
        /* if cuserid() didn't work, then check the environment */
        if(strcmp(cuser, "") == 0)
821
        {
Shane Snyder's avatar
Shane Snyder committed
822 823 824 825 826
            logname_string = getenv("LOGNAME");
            if(logname_string)
            {
                strncpy(cuser, logname_string, (L_cuserid-1));
            }
827 828
        }

Shane Snyder's avatar
Shane Snyder committed
829 830 831 832 833 834
        /* if cuserid() and environment both fail, then fall back to uid */
        if(strcmp(cuser, "") == 0)
        {
            uid_t uid = geteuid();
            snprintf(cuser, sizeof(cuser), "%u", uid);
        }
835

Shane Snyder's avatar
Shane Snyder committed
836 837 838 839
        /* generate a random number to help differentiate the log */
        hlevel=DARSHAN_MPI_CALL(PMPI_Wtime)() * 1000000;
        (void)gethostname(hname, sizeof(hname));
        logmod = darshan_hash((void*)hname,strlen(hname),hlevel);
840

Shane Snyder's avatar
Shane Snyder committed
841 842 843 844
        /* see if darshan was configured using the --with-logpath-by-env
         * argument, which allows the user to specify an absolute path to
         * place logs via an env variable.
         */
845
#ifdef __DARSHAN_LOG_ENV
Shane Snyder's avatar
Shane Snyder committed
846
        /* just silently skip if the environment variable list is too big */
847
        if(strlen(__DARSHAN_LOG_ENV) < 256)
848
        {
Shane Snyder's avatar
Shane Snyder committed
849
            /* copy env variable list to a temporary buffer */
850
            strcpy(env_check, __DARSHAN_LOG_ENV);
Shane Snyder's avatar
Shane Snyder committed
851 852 853
            /* tokenize the comma-separated list */
            env_tok = strtok(env_check, ",");
            if(env_tok)
854
            {
Shane Snyder's avatar
Shane Snyder committed
855
                do
856
                {
Shane Snyder's avatar
Shane Snyder committed
857 858 859 860 861 862 863 864 865
                    /* check each env variable in order */
                    logpath_override = getenv(env_tok);
                    if(logpath_override)
                    {
                        /* stop as soon as we find a match */
                        break;
                    }
                }while((env_tok = strtok(NULL, ",")));
            }
866 867 868
        }
#endif

Shane Snyder's avatar
Shane Snyder committed
869
        if(logpath_override)
870
        {
Shane Snyder's avatar
Shane Snyder committed
871 872 873 874 875 876 877 878 879 880 881 882 883 884 885
            ret = snprintf(logfile_name, PATH_MAX,
                "%s/%s_%s_id%d_%d-%d-%d-%" PRIu64 ".darshan_partial",
                logpath_override,
                cuser, __progname, jobid,
                (start_tm->tm_mon+1),
                start_tm->tm_mday,
                (start_tm->tm_hour*60*60 + start_tm->tm_min*60 + start_tm->tm_sec),
                logmod);
            if(ret == (PATH_MAX-1))
            {
                /* file name was too big; squish it down */
                snprintf(logfile_name, PATH_MAX,
                    "%s/id%d.darshan_partial",
                    logpath_override, jobid);
            }
886
        }
Shane Snyder's avatar
Shane Snyder committed
887
        else if(logpath)
888
        {
Shane Snyder's avatar
Shane Snyder committed
889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908
            ret = snprintf(logfile_name, PATH_MAX,
                "%s/%d/%d/%d/%s_%s_id%d_%d-%d-%d-%" PRIu64 ".darshan_partial",
                logpath, (start_tm->tm_year+1900),
                (start_tm->tm_mon+1), start_tm->tm_mday,
                cuser, __progname, jobid,
                (start_tm->tm_mon+1),
                start_tm->tm_mday,
                (start_tm->tm_hour*60*60 + start_tm->tm_min*60 + start_tm->tm_sec),
                logmod);
            if(ret == (PATH_MAX-1))
            {
                /* file name was too big; squish it down */
                snprintf(logfile_name, PATH_MAX,
                    "%s/id%d.darshan_partial",
                    logpath, jobid);
            }
        }
        else
        {
            logfile_name[0] = '\0';
909 910 911 912
        }
    }

    return;
913 914
}

915
/* record any hints used to write the darshan log in the log header */
916
static void darshan_log_record_hints_and_ver(struct darshan_core_runtime* core)
917 918 919 920 921 922 923 924 925
{
    char* hints;
    char* header_hints;
    int meta_remain = 0;
    char* m;

    /* check environment variable to see if the default MPI file hints have
     * been overridden
     */
926
    hints = getenv(DARSHAN_LOG_HINTS_OVERRIDE);
927 928