/* * Copyright (C) 2015 University of Chicago. * See COPYRIGHT notice in top-level directory. * */ #define _XOPEN_SOURCE 500 #define _GNU_SOURCE #include "darshan-runtime-config.h" #include #ifdef HAVE_MNTENT_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "uthash.h" #include "darshan.h" #include "darshan-core.h" #include "darshan-dynamic.h" extern char* __progname; extern char* __progname_full; /* internal variable delcarations */ static struct darshan_core_runtime *darshan_core = NULL; static pthread_mutex_t darshan_core_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; static int my_rank = -1; static int nprocs = -1; static int darshan_mem_alignment = 1; /* 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 }; #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 }; #define DARSHAN_CORE_LOCK() pthread_mutex_lock(&darshan_core_mutex) #define DARSHAN_CORE_UNLOCK() pthread_mutex_unlock(&darshan_core_mutex) /* FS mount information */ #define DARSHAN_MAX_MNTS 64 #define DARSHAN_MAX_MNT_PATH 256 #define DARSHAN_MAX_MNT_TYPE 32 struct mnt_data { int block_size; 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; /* prototypes for internal helper functions */ static void darshan_log_record_hints_and_ver( struct darshan_core_runtime* core); static void darshan_get_exe_and_mounts( struct darshan_core_runtime *core, int argc, char **argv); static void darshan_add_name_record_ref( struct darshan_core_runtime *core, darshan_record_id rec_id, char *name, darshan_module_id mod_id); static int darshan_block_size_from_path( const char *path); static void darshan_get_user_name( char *user); static void darshan_get_logfile_name( char* logfile_name, int jobid, struct tm* start_tm); static void darshan_get_shared_records( struct darshan_core_runtime *core, darshan_record_id **shared_recs, int *shared_rec_cnt); static int darshan_log_open_all( char *logfile_name, MPI_File *log_fh); static int darshan_deflate_buffer( void **pointers, int *lengths, int count, char *comp_buf, int *comp_buf_length); static int darshan_log_write_name_record_hash( MPI_File log_fh, struct darshan_core_runtime *core, uint64_t *inout_off); static int darshan_log_append_all( MPI_File log_fh, struct darshan_core_runtime *core, void *buf, int count, uint64_t *inout_off); static void darshan_core_cleanup( struct darshan_core_runtime* core); /* *********************************** */ void darshan_core_initialize(int argc, char **argv) { struct darshan_core_runtime *init_core = NULL; int internal_timing_flag = 0; double init_start, init_time, init_max; char *envstr; char *jobid_str; int jobid; int ret; int tmpval; int i; #ifdef __DARSHAN_ENABLE_MMAP_LOGS int mmap_fd; int mmap_size; int sys_page_size; char cuser[L_cuserid] = {0}; char *mmap_log_dir; #endif DARSHAN_MPI_CALL(PMPI_Comm_size)(MPI_COMM_WORLD, &nprocs); DARSHAN_MPI_CALL(PMPI_Comm_rank)(MPI_COMM_WORLD, &my_rank); if(getenv("DARSHAN_INTERNAL_TIMING")) internal_timing_flag = 1; if(internal_timing_flag) init_start = DARSHAN_MPI_CALL(PMPI_Wtime)(); /* setup darshan runtime if darshan is enabled and hasn't been initialized already */ if(!getenv("DARSHAN_DISABLE") && !darshan_core) { #if (__DARSHAN_MEM_ALIGNMENT < 1) #error Darshan must be configured with a positive value for --with-mem-align #endif envstr = getenv(DARSHAN_MEM_ALIGNMENT_OVERRIDE); 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 { darshan_mem_alignment = __DARSHAN_MEM_ALIGNMENT; } /* avoid floating point errors on faulty input */ if(darshan_mem_alignment < 1) { darshan_mem_alignment = 1; } /* Use DARSHAN_JOBID_OVERRIDE for the env var for __DARSHAN_JOBID */ envstr = getenv(DARSHAN_JOBID_OVERRIDE); if(!envstr) { envstr = __DARSHAN_JOBID; } /* 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(); } /* allocate structure to track darshan core runtime information */ init_core = malloc(sizeof(*init_core)); if(init_core) { memset(init_core, 0, sizeof(*init_core)); init_core->wtime_offset = DARSHAN_MPI_CALL(PMPI_Wtime)(); #ifndef __DARSHAN_ENABLE_MMAP_LOGS /* just allocate memory for each log file region */ init_core->log_hdr_p = malloc(sizeof(struct darshan_header)); init_core->log_job_p = malloc(sizeof(struct darshan_job)); init_core->log_exemnt_p = malloc(DARSHAN_EXE_LEN+1); init_core->log_name_p = malloc(DARSHAN_NAME_RECORD_BUF_SIZE); init_core->log_mod_p = malloc(DARSHAN_MOD_MEM_MAX); if(!(init_core->log_hdr_p) || !(init_core->log_job_p) || !(init_core->log_exemnt_p) || !(init_core->log_name_p) || !(init_core->log_mod_p)) { free(init_core); return; } /* if allocation succeeds, zero fill memory regions */ memset(init_core->log_hdr_p, 0, sizeof(struct darshan_header)); memset(init_core->log_job_p, 0, sizeof(struct darshan_job)); memset(init_core->log_exemnt_p, 0, DARSHAN_EXE_LEN+1); memset(init_core->log_name_p, 0, DARSHAN_NAME_RECORD_BUF_SIZE); memset(init_core->log_mod_p, 0, DARSHAN_MOD_MEM_MAX); #else /* if mmap logs are enabled, we need to initialize the mmap region * before setting the corresponding log file region pointers */ sys_page_size = sysconf(_SC_PAGESIZE); assert(sys_page_size > 0); mmap_size = sizeof(struct darshan_header) + DARSHAN_JOB_RECORD_SIZE + + DARSHAN_NAME_RECORD_BUF_SIZE + DARSHAN_MOD_MEM_MAX; if(mmap_size % sys_page_size) mmap_size = ((mmap_size / sys_page_size) + 1) * sys_page_size; envstr = getenv(DARSHAN_MMAP_LOG_PATH_OVERRIDE); if(envstr) mmap_log_dir = envstr; else mmap_log_dir = DARSHAN_DEF_MMAP_LOG_PATH; darshan_get_user_name(cuser); /* construct a unique temporary log file name for this process * to write mmap log data to */ snprintf(init_core->mmap_log_name, PATH_MAX, "/%s/%s_%s_id%d_mmap-log-%d.darshan", mmap_log_dir, cuser, __progname, jobid, my_rank); /* create the temporary mmapped darshan log */ mmap_fd = open(init_core->mmap_log_name, O_CREAT|O_RDWR|O_EXCL , 0644); if(mmap_fd < 0) { fprintf(stderr, "darshan library warning: " "unable to create darshan log file %s\n", init_core->mmap_log_name); free(init_core); return; } /* TODO: what's more expensive? truncate or write zeros? perf test this call and later accesses */ /* 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", init_core->mmap_log_name); free(init_core); close(mmap_fd); unlink(init_core->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 */ void *mmap_p = mmap(NULL, mmap_size, PROT_WRITE, MAP_SHARED, mmap_fd, 0); if(mmap_p == MAP_FAILED) { fprintf(stderr, "darshan library warning: " "unable to mmap darshan log file %s\n", init_core->mmap_log_name); free(init_core); close(mmap_fd); unlink(init_core->mmap_log_name); return; } /* close darshan log file (this does *not* unmap the log file) */ close(mmap_fd); /* set the memory pointers for each log file region */ init_core->log_hdr_p = (struct darshan_header *)mmap_p; init_core->log_job_p = (struct darshan_job *) ((char *)init_core->log_hdr_p + sizeof(struct darshan_header)); init_core->log_exemnt_p = (char *) ((char *)init_core->log_job_p + sizeof(struct darshan_job)); init_core->log_name_p = (void *) ((char *)init_core->log_exemnt_p + DARSHAN_EXE_LEN + 1); init_core->log_mod_p = (void *) ((char *)init_core->log_name_p + DARSHAN_NAME_RECORD_BUF_SIZE); /* set header fields needed for the mmap log mechanism */ init_core->log_hdr_p->comp_type = DARSHAN_NO_COMP; init_core->log_hdr_p->name_map.off = sizeof(struct darshan_header) + DARSHAN_JOB_RECORD_SIZE; #endif /* set known header fields for the log file */ strcpy(init_core->log_hdr_p->version_string, DARSHAN_LOG_VERSION); init_core->log_hdr_p->magic_nr = DARSHAN_MAGIC_NR; /* set known job-level metadata fields for the log file */ init_core->log_job_p->uid = getuid(); init_core->log_job_p->start_time = time(NULL); init_core->log_job_p->nprocs = nprocs; init_core->log_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); /* collect information about command line and mounted file systems */ darshan_get_exe_and_mounts(init_core, argc, argv); /* bootstrap any modules with static initialization routines */ i = 0; while(mod_static_init_fns[i]) { (*mod_static_init_fns[i])(); i++; } darshan_core = init_core; } } /* reduce so all ranks agree on the job start time */ DARSHAN_MPI_CALL(PMPI_Allreduce)(MPI_IN_PLACE, &init_core->log_job_p->start_time, 1, MPI_INT64_T, MPI_MIN, MPI_COMM_WORLD); 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); if(my_rank == 0) { fprintf(stderr, "#darshan:\t\t