From df9e497e76df145b81ca07452932c3516d3bf571 Mon Sep 17 00:00:00 2001 From: David Bigagli <david@schedmd.com> Date: Fri, 2 Aug 2013 10:15:51 -0700 Subject: [PATCH] Enhance sh5util to extract data items from the merged file. --- doc/man/man1/sh5util.1 | 93 ++- .../acct_gather_profile/hdf5/hdf5_api.c | 340 +++++++++-- .../acct_gather_profile/hdf5/hdf5_api.h | 5 + .../hdf5/sh5util/sh5util.c | 544 +++++++++++++++++- 4 files changed, 932 insertions(+), 50 deletions(-) diff --git a/doc/man/man1/sh5util.1 b/doc/man/man1/sh5util.1 index d240f99b553..a17b8dd58c6 100644 --- a/doc/man/man1/sh5util.1 +++ b/doc/man/man1/sh5util.1 @@ -14,10 +14,19 @@ sh5util merges HDF5 files produced on each node for each step of a job into one HDF5 file for the job. The resulting file can be viewed and manipulated by common HDF5 tools such as HDF5View, h5dump, h5edit, or h5ls. .LP -sh5util also has an extract mode that writes a limited set of +sh5util also has two extract modes. The first, writes a limited set of data for specific nodes, steps, and data series in "comma separated value" form to a file which can be imported into other analysis tools such as spreadsheets. +.LP +The second, (Item-Extract) extracts one data time from one time series for all +the samples on all the nodes from a jobs HDF5 profile. +.TP +\- Finds sample with maximum value of the item. +.TP +\- Write CSV file with min, ave, max, and item totals for each node for each +sample + .SH "OPTIONS" .LP @@ -30,7 +39,7 @@ extract data series from a job file. .RS .TP 10 -Extract mode options (all imply --extract) +Extract mode options .TP \fB\-i\fR, \fB\-\-input\fR=\fIpath\fR @@ -45,8 +54,28 @@ Node name to extract (default is all) Level to which series is attached. (default Node:Totals) .TP -\fB\-s\fR, \fB\-\-series\fR=\fI[Name|Tasks]\fR -Name=Specific Name, Tasks=all tasks, (default is everything) +\fB\-s\fR, \fB\-\-series\fR=\fI[Energy | Lustre | Network | Tasks | Task_#]\fR +\fBTasks\fR is all tasks, \fBTask_#\fR (# is a task id) (default is everything) + +.RE + +.TP +\fB\-I\fR, \fB\-\-item\-extract\fR + +Instead of merging node-step files into a job file extract one data item from +all samples of one data series from all nodes in a job file. + +.RS +.TP 10 +Item-Extract mode options + +.TP +\fB\-s\fR, \fB\-\-series\fR=[Energy | Lustre | Network | Task]\fR + +.TP +\fB\-d\fR, \fB\-\-data\fR +Name of data item in series (See note below). + .RE .TP @@ -86,6 +115,62 @@ User who profiled job. \fB\-\-usage\fR Display brief usage message. +.SH "Data Items per Series" + +.TP +\fBEnergy\fR +.nf +Power +CPU_Frequency +.fi + +.TP +\fBLustre\fR +.nf +Reads +Megabytes_Read +Writes +Megabytes_Write +.fi + +.TP +\fBNetwork\fR +.nf +Packets_In +Megabytes_In +Packets_Out +Megabytes_Out +.fi + +.TP +\fBTask\fR +.nf +CPU_Frequency +CPU_Time +CPU_Utilization +RSS +VM_Size +Pages +Read_Megabytes +Write_Megabytes +.fi + +.SH "Examples" + +.TP +Merge node-step files (as part of a sbatch script) +.LP +sbatch -n1 -d$SLURM_JOB_ID --wrap="sh5util --savefiles -j $SLURM_JOB_ID" + +.TP +Extract all task data from a node +.LP +sh5util -j 42 -N snowflake01 --level=Node:TimeSeries --series=Tasks + +.TP +Extract all energy data +sh5util -j 42 --series=Energy --data=power + .SH "COPYING" Copyright (C) 2013 Bull. .br diff --git a/src/plugins/acct_gather_profile/hdf5/hdf5_api.c b/src/plugins/acct_gather_profile/hdf5/hdf5_api.c index c4be66bcbc6..013d6dd8030 100644 --- a/src/plugins/acct_gather_profile/hdf5/hdf5_api.c +++ b/src/plugins/acct_gather_profile/hdf5/hdf5_api.c @@ -225,10 +225,10 @@ static hid_t _energy_create_memory_datatype(void) debug3("PROFILE: failed to create Energy memory datatype"); return -1; } - MEM_ADD_DATE_TIME(mtyp_energy, "Date Time", profile_energy_t, tod); + MEM_ADD_DATE_TIME(mtyp_energy, "Date_Time", profile_energy_t, tod); MEM_ADD_UINT64(mtyp_energy, "Time", profile_energy_t, time); MEM_ADD_UINT64(mtyp_energy, "Power", profile_energy_t, power); - MEM_ADD_UINT64(mtyp_energy, "CPU Frequency", + MEM_ADD_UINT64(mtyp_energy, "CPU_Frequency", profile_energy_t, cpu_freq); return mtyp_energy; @@ -242,10 +242,10 @@ static hid_t _energy_create_file_datatype(void) return -1; } moffset = TOD_LEN; - FILE_ADD_DATE_TIME(ftyp_energy, "Date Time", 0); + FILE_ADD_DATE_TIME(ftyp_energy, "Date_Time", 0); FILE_ADD_UINT64(ftyp_energy, "Time"); FILE_ADD_UINT64(ftyp_energy, "Power"); - FILE_ADD_UINT64(ftyp_energy, "CPU Frequency"); + FILE_ADD_UINT64(ftyp_energy, "CPU_Frequency"); return ftyp_energy; } @@ -313,6 +313,56 @@ static void *_energy_init_job_series(int n_samples) return (void*) energy_data; } +static char** _energy_get_series_tod(void* data, int nsmp) +{ + int ix; + char **tod_values = NULL; + profile_energy_t* energy_series = (profile_energy_t*) data; + tod_values = (char**) xmalloc(nsmp*sizeof(char*)); + if (tod_values == NULL) { + info("Failed to get memory for energy tod"); + return NULL; + } + for (ix=0; ix < nsmp; ix++) { + tod_values[ix] = xstrdup(energy_series[ix].tod); + } + return tod_values; +} + +static double* _energy_get_series_values(char* data_name, void* data, int nsmp) +{ + int ix; + profile_energy_t* energy_series = (profile_energy_t*) data; + double *energy_values = NULL; + energy_values = xmalloc(nsmp*sizeof(double)); + if (energy_values == NULL) { + info("PROFILE: Failed to get memory for energy data"); + return NULL; + } + if (strcasecmp(data_name,"Time") == 0) { + for (ix=0; ix < nsmp; ix++) { + energy_values[ix] = (double) energy_series[ix].time; + + } + return energy_values; + } else if (strcasecmp(data_name,"Power") == 0) { + for (ix=0; ix < nsmp; ix++) { + energy_values[ix] = (double) energy_series[ix].power; + + } + return energy_values; + } else if (strcasecmp(data_name,"CPU_Frequency") == 0) { + for (ix=0; ix < nsmp; ix++) { + energy_values[ix] = (double) energy_series[ix].cpu_freq; + + } + return energy_values; + } + xfree(energy_values); + info("PROFILE: %s is invalid data item for energy data", data_name); + return NULL; +} + static void _energy_merge_step_series( hid_t group, void *prior, void *cur, void *buf) { @@ -402,6 +452,8 @@ static hdf5_api_ops_t* _energy_profile_factory(void) ops->create_s_memory_datatype = &_energy_s_create_memory_datatype; ops->create_s_file_datatype = &_energy_s_create_file_datatype; ops->init_job_series = &_energy_init_job_series; + ops->get_series_tod = &_energy_get_series_tod; + ops->get_series_values = &_energy_get_series_values; ops->merge_step_series = &_energy_merge_step_series; ops->series_total = &_energy_series_total; ops->extract_series = &_energy_extract_series; @@ -428,12 +480,12 @@ static hid_t _io_create_memory_datatype(void) debug3("PROFILE: failed to create IO memory datatype"); return -1; } - MEM_ADD_DATE_TIME(mtyp_io, "Date Time", profile_io_t, tod); + MEM_ADD_DATE_TIME(mtyp_io, "Date_Time", profile_io_t, tod); MEM_ADD_UINT64(mtyp_io, "Time", profile_io_t, time); MEM_ADD_UINT64(mtyp_io, "Reads", profile_io_t, reads); - MEM_ADD_DBL(mtyp_io, "Megabytes Read", profile_io_t, read_size); + MEM_ADD_DBL(mtyp_io, "Megabytes_Read", profile_io_t, read_size); MEM_ADD_UINT64(mtyp_io, "Writes", profile_io_t, writes); - MEM_ADD_DBL(mtyp_io, "Megabytes Write", profile_io_t, write_size); + MEM_ADD_DBL(mtyp_io, "Megabytes_Write", profile_io_t, write_size); return mtyp_io; } @@ -447,12 +499,12 @@ static hid_t _io_create_file_datatype(void) return -1; } moffset = TOD_LEN; - FILE_ADD_DATE_TIME(ftyp_io, "Date Time", 0); + FILE_ADD_DATE_TIME(ftyp_io, "Date_Time", 0); FILE_ADD_UINT64(ftyp_io, "Time"); FILE_ADD_UINT64(ftyp_io, "Reads"); - FILE_ADD_DBL(ftyp_io, "Megabytes Read"); + FILE_ADD_DBL(ftyp_io, "Megabytes_Read"); FILE_ADD_UINT64(ftyp_io, "Writes"); - FILE_ADD_DBL(ftyp_io, "Megabytes Write"); + FILE_ADD_DBL(ftyp_io, "Megabytes_Write"); return ftyp_io; } @@ -539,6 +591,68 @@ static void *_io_init_job_series(int n_samples) return (void*) io_data; } +static char** _io_get_series_tod(void* data, int nsmp) +{ + int ix; + char **tod_values = NULL; + profile_io_t* io_series = (profile_io_t*) data; + tod_values = (char**) xmalloc(nsmp*sizeof(char*)); + if (tod_values == NULL) { + info("Failed to get memory for io tod"); + return NULL; + } + for (ix=0; ix < nsmp; ix++) { + tod_values[ix] = xstrdup(io_series[ix].tod); + } + return tod_values; +} + +static double* _io_get_series_values(char* data_name, void* data, int nsmp) +{ + int ix; + profile_io_t* io_series = (profile_io_t*) data; + double *io_values = NULL; + io_values = xmalloc(nsmp*sizeof(double)); + if (io_values == NULL) { + info("PROFILE: Failed to get memory for io data"); + return NULL; + } + if (strcasecmp(data_name,"Time") == 0) { + for (ix=0; ix < nsmp; ix++) { + io_values[ix] = (double) io_series[ix].time; + + } + return io_values; + } else if (strcasecmp(data_name,"Reads") == 0) { + for (ix=0; ix < nsmp; ix++) { + io_values[ix] = (double) io_series[ix].reads; + + } + return io_values; + } else if (strcasecmp(data_name,"Megabytes_Read") == 0) { + for (ix=0; ix < nsmp; ix++) { + io_values[ix] = io_series[ix].read_size; + + } + return io_values; + } else if (strcasecmp(data_name,"Writes") == 0) { + for (ix=0; ix < nsmp; ix++) { + io_values[ix] = (double) io_series[ix].writes; + + } + return io_values; + } else if (strcasecmp(data_name,"Megabytes_Write") == 0) { + for (ix=0; ix < nsmp; ix++) { + io_values[ix] = io_series[ix].write_size; + + } + return io_values; + } + xfree(io_values); + info("PROFILE: %s is invalid data item for io data", data_name); + return NULL; +} + static void _io_merge_step_series( hid_t group, void *prior, void *cur, void *buf) { @@ -650,6 +764,8 @@ static hdf5_api_ops_t* _io_profile_factory(void) ops->create_s_memory_datatype = &_io_s_create_memory_datatype; ops->create_s_file_datatype = &_io_s_create_file_datatype; ops->init_job_series = &_io_init_job_series; + ops->get_series_tod = &_io_get_series_tod; + ops->get_series_values = &_io_get_series_values; ops->merge_step_series = &_io_merge_step_series; ops->series_total = &_io_series_total; ops->extract_series = &_io_extract_series; @@ -675,14 +791,14 @@ static hid_t _network_create_memory_datatype(void) debug3("PROFILE: failed to create Network memory datatype"); return -1; } - MEM_ADD_DATE_TIME(mtyp_network, "Date Time", profile_network_t, tod); + MEM_ADD_DATE_TIME(mtyp_network, "Date_Time", profile_network_t, tod); MEM_ADD_UINT64(mtyp_network, "Time", profile_network_t, time); - MEM_ADD_UINT64(mtyp_network, "Packets In", + MEM_ADD_UINT64(mtyp_network, "Packets_In", profile_network_t, packets_in); - MEM_ADD_DBL(mtyp_network, "Megabytes In", profile_network_t, size_in); - MEM_ADD_UINT64(mtyp_network, "Packets Out", + MEM_ADD_DBL(mtyp_network, "Megabytes_In", profile_network_t, size_in); + MEM_ADD_UINT64(mtyp_network, "Packets_Out", profile_network_t, packets_out); - MEM_ADD_DBL(mtyp_network, "Megabytes Out", profile_network_t, size_out); + MEM_ADD_DBL(mtyp_network, "Megabytes_Out", profile_network_t, size_out); return mtyp_network; } @@ -695,12 +811,12 @@ static hid_t _network_create_file_datatype(void) return -1; } moffset = TOD_LEN; - FILE_ADD_DATE_TIME(ftyp_network, "Date Time", 0); + FILE_ADD_DATE_TIME(ftyp_network, "Date_Time", 0); FILE_ADD_UINT64(ftyp_network, "Time"); - FILE_ADD_UINT64(ftyp_network, "Packets In"); - FILE_ADD_DBL(ftyp_network, "Megabytes In"); - FILE_ADD_UINT64(ftyp_network, "Packets Out"); - FILE_ADD_DBL(ftyp_network, "Megabytes Out"); + FILE_ADD_UINT64(ftyp_network, "Packets_In"); + FILE_ADD_DBL(ftyp_network, "Megabytes_In"); + FILE_ADD_UINT64(ftyp_network, "Packets_Out"); + FILE_ADD_DBL(ftyp_network, "Megabytes_Out"); return ftyp_network; } @@ -796,6 +912,70 @@ static void *_network_init_job_series(int n_samples) return (void*) network_data; } +static char** _network_get_series_tod(void* data, int nsmp) +{ + int ix; + char **tod_values = NULL; + profile_network_t* network_series = (profile_network_t*) data; + tod_values = (char**) xmalloc(nsmp*sizeof(char*)); + if (tod_values == NULL) { + info("Failed to get memory for network tod"); + return NULL; + } + for (ix=0; ix < nsmp; ix++) { + tod_values[ix] = xstrdup(network_series[ix].tod); + } + return tod_values; +} + +static double* _network_get_series_values(char* data_name, void* data, int nsmp) +{ + int ix; + profile_network_t* network_series = (profile_network_t*) data; + double *network_values = NULL; + network_values = xmalloc(nsmp*sizeof(double)); + if (network_values == NULL) { + info("PROFILE: Failed to get memory for network data"); + return NULL; + } + if (strcasecmp(data_name,"Time") == 0) { + for (ix=0; ix < nsmp; ix++) { + network_values[ix] = (double) network_series[ix].time; + + } + return network_values; + } else if (strcasecmp(data_name,"Packets_In") == 0) { + for (ix=0; ix < nsmp; ix++) { + network_values[ix] = + (double) network_series[ix].packets_in; + + } + return network_values; + } else if (strcasecmp(data_name,"Megabytes_In") == 0) { + for (ix=0; ix < nsmp; ix++) { + network_values[ix] = network_series[ix].size_in; + + } + return network_values; + } else if (strcasecmp(data_name,"Packets_Out") == 0) { + for (ix=0; ix < nsmp; ix++) { + network_values[ix] = + (double) network_series[ix].packets_out; + + } + return network_values; + } else if (strcasecmp(data_name,"Megabytes_Out") == 0) { + for (ix=0; ix < nsmp; ix++) { + network_values[ix] = network_series[ix].size_out; + + } + return network_values; + } + xfree(network_values); + info("PROFILE: %s is invalid data item for network data", data_name); + return NULL; +} + static void _network_merge_step_series( hid_t group, void *prior, void *cur, void *buf) { @@ -827,7 +1007,7 @@ static void *_network_series_total(int n_samples, void *data) network_data = (profile_network_t*) data; total = xmalloc(sizeof(profile_network_s_t)); if (total == NULL) { - error("PROFILE: Out of memory getting energy total"); + error("PROFILE: Out of memory getting network total"); return NULL; } // Assuming network series are a running total, and the first @@ -899,6 +1079,8 @@ static hdf5_api_ops_t *_network_profile_factory(void) ops->create_s_memory_datatype = &_network_s_create_memory_datatype; ops->create_s_file_datatype = &_network_s_create_file_datatype; ops->init_job_series = &_network_init_job_series; + ops->get_series_tod = &_network_get_series_tod; + ops->get_series_values = &_network_get_series_values; ops->merge_step_series = &_network_merge_step_series; ops->series_total = &_network_series_total; ops->extract_series = &_network_extract_series; @@ -922,17 +1104,17 @@ static hid_t _task_create_memory_datatype(void) debug3("PROFILE: failed to create Task memory datatype"); return -1; } - MEM_ADD_DATE_TIME(mtyp_task, "Date Time", profile_task_t, tod); + MEM_ADD_DATE_TIME(mtyp_task, "Date_Time", profile_task_t, tod); MEM_ADD_UINT64(mtyp_task, "Time", profile_task_t, time); - MEM_ADD_UINT64(mtyp_task, "CPU Frequency", profile_task_t, cpu_freq); - MEM_ADD_UINT64(mtyp_task, "CPU Time", profile_task_t, cpu_time); - MEM_ADD_DBL(mtyp_task, "CPU Utilization", + MEM_ADD_UINT64(mtyp_task, "CPU_Frequency", profile_task_t, cpu_freq); + MEM_ADD_UINT64(mtyp_task, "CPU_Time", profile_task_t, cpu_time); + MEM_ADD_DBL(mtyp_task, "CPU_Utilization", profile_task_t, cpu_utilization); MEM_ADD_UINT64(mtyp_task, "RSS", profile_task_t, rss); - MEM_ADD_UINT64(mtyp_task, "VM Size", profile_task_t, vm_size); + MEM_ADD_UINT64(mtyp_task, "VM_Size", profile_task_t, vm_size); MEM_ADD_UINT64(mtyp_task, "Pages", profile_task_t, pages); - MEM_ADD_DBL(mtyp_task, "Read Megabytes", profile_task_t, read_size); - MEM_ADD_DBL(mtyp_task, "Write Megabytes", profile_task_t, write_size); + MEM_ADD_DBL(mtyp_task, "Read_Megabytes", profile_task_t, read_size); + MEM_ADD_DBL(mtyp_task, "Write_Megabytes", profile_task_t, write_size); return mtyp_task; } @@ -945,16 +1127,16 @@ static hid_t _task_create_file_datatype(void) return -1; } moffset = TOD_LEN; - FILE_ADD_DATE_TIME(ftyp_task, "Date Time", 0); + FILE_ADD_DATE_TIME(ftyp_task, "Date_Time", 0); FILE_ADD_UINT64(ftyp_task, "Time"); - FILE_ADD_UINT64(ftyp_task, "CPU Frequency"); - FILE_ADD_UINT64(ftyp_task, "CPU Time"); - FILE_ADD_DBL(ftyp_task, "CPU Utilization"); + FILE_ADD_UINT64(ftyp_task, "CPU_Frequency"); + FILE_ADD_UINT64(ftyp_task, "CPU_Time"); + FILE_ADD_DBL(ftyp_task, "CPU_Utilization"); FILE_ADD_UINT64(ftyp_task, "RSS"); - FILE_ADD_UINT64(ftyp_task, "VM Size"); + FILE_ADD_UINT64(ftyp_task, "VM_Size"); FILE_ADD_UINT64(ftyp_task, "Pages"); - FILE_ADD_DBL(ftyp_task, "Read Megabytes"); - FILE_ADD_DBL(ftyp_task, "Write Megabytes"); + FILE_ADD_DBL(ftyp_task, "Read_Megabytes"); + FILE_ADD_DBL(ftyp_task, "Write_Megabytes"); return ftyp_task; } @@ -1084,6 +1266,92 @@ static void *_task_init_job_series(int n_samples) return (void*) task_data; } +static char** _task_get_series_tod(void* data, int nsmp) +{ + int ix; + char **tod_values = NULL; + profile_task_t* task_series = (profile_task_t*) data; + tod_values = (char**) xmalloc(nsmp*sizeof(char*)); + if (tod_values == NULL) { + info("Failed to get memory for task tod"); + return NULL; + } + for (ix=0; ix < nsmp; ix++) { + tod_values[ix] = xstrdup(task_series[ix].tod); + } + return tod_values; +} + +static double* _task_get_series_values(char* data_name, void* data, int nsmp) +{ + int ix; + profile_task_t* task_series = (profile_task_t*) data; + double *task_values = NULL; + task_values = xmalloc(nsmp*sizeof(double)); + if (task_values == NULL) { + info("PROFILE: Failed to get memory for task data"); + return NULL; + } + if (strcasecmp(data_name,"Time") == 0) { + for (ix=0; ix < nsmp; ix++) { + task_values[ix] = (double) task_series[ix].time; + + } + return task_values; + } else if (strcasecmp(data_name,"CPU_Frequency") == 0) { + for (ix=0; ix < nsmp; ix++) { + task_values[ix] = (double) task_series[ix].cpu_freq; + + } + return task_values; + } else if (strcasecmp(data_name,"CPU_Time") == 0) { + for (ix=0; ix < nsmp; ix++) { + task_values[ix] = (double) task_series[ix].cpu_time; + + } + return task_values; + } else if (strcasecmp(data_name,"CPU_Utilization") == 0) { + for (ix=0; ix < nsmp; ix++) { + task_values[ix] = task_series[ix].cpu_utilization; + + } + return task_values; + } else if (strcasecmp(data_name,"RSS") == 0) { + for (ix=0; ix < nsmp; ix++) { + task_values[ix] = (double) task_series[ix].rss; + + } + return task_values; + } else if (strcasecmp(data_name,"VM_Size") == 0) { + for (ix=0; ix < nsmp; ix++) { + task_values[ix] = (double) task_series[ix].vm_size; + + } + return task_values; + } else if (strcasecmp(data_name,"Pages") == 0) { + for (ix=0; ix < nsmp; ix++) { + task_values[ix] = (double) task_series[ix].pages; + + } + return task_values; + } else if (strcasecmp(data_name,"Read_Megabytes") == 0) { + for (ix=0; ix < nsmp; ix++) { + task_values[ix] = task_series[ix].read_size; + + } + return task_values; + } else if (strcasecmp(data_name,"Write_Megabytes") == 0) { + for (ix=0; ix < nsmp; ix++) { + task_values[ix] = task_series[ix].write_size; + + } + return task_values; + } + xfree(task_values); + info("PROFILE: %s is invalid data item for task data", data_name); + return NULL; +} + static void _task_merge_step_series( hid_t group, void *prior, void *cur, void *buf) { @@ -1216,6 +1484,8 @@ static hdf5_api_ops_t *_task_profile_factory(void) ops->create_s_memory_datatype = &_task_s_create_memory_datatype; ops->create_s_file_datatype = &_task_s_create_file_datatype; ops->init_job_series = &_task_init_job_series; + ops->get_series_tod = &_task_get_series_tod; + ops->get_series_values = &_task_get_series_values; ops->merge_step_series = &_task_merge_step_series; ops->series_total = &_task_series_total; ops->extract_series = &_task_extract_series; diff --git a/src/plugins/acct_gather_profile/hdf5/hdf5_api.h b/src/plugins/acct_gather_profile/hdf5/hdf5_api.h index de69d4cf2f7..721bb189735 100644 --- a/src/plugins/acct_gather_profile/hdf5/hdf5_api.h +++ b/src/plugins/acct_gather_profile/hdf5/hdf5_api.h @@ -223,6 +223,9 @@ typedef struct profile_task_s { * to the summary datatype structure. * init_job_series -- allocates a buffer for a complete time series * (in job merge) and initializes each member + * get_series_tod -- get the date/time value of each sample in the series + * get_series_values -- gets a specific data item from each sample in the + * series * merge_step_series -- merges all the individual time samples into a * single data set with one item per sample. * Data items can be scaled (e.g. subtracting beginning time) @@ -245,6 +248,8 @@ typedef struct hdf5_api_ops { hid_t (*create_s_memory_datatype) (void); hid_t (*create_s_file_datatype) (void); void* (*init_job_series) (int); + char** (*get_series_tod) (void*, int); + double* (*get_series_values) (char*, void*, int); void (*merge_step_series) (hid_t, void*, void*, void*); void* (*series_total) (int, void*); void (*extract_series) (FILE*, bool, int, int, char*, char*, void*, diff --git a/src/plugins/acct_gather_profile/hdf5/sh5util/sh5util.c b/src/plugins/acct_gather_profile/hdf5/sh5util/sh5util.c index df3c0bd94de..a2bb167ddfc 100644 --- a/src/plugins/acct_gather_profile/hdf5/sh5util/sh5util.c +++ b/src/plugins/acct_gather_profile/hdf5/sh5util/sh5util.c @@ -40,10 +40,6 @@ * You should have received a copy of the GNU General Public License along * with SLURM; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - ***************************************************************************** - * - * This program is expected to be launched by the SLURM epilog script for a - * job on the controller node to merge node-step files into a job file. * \*****************************************************************************/ @@ -78,6 +74,7 @@ typedef enum { SH5UTIL_MODE_MERGE, SH5UTIL_MODE_EXTRACT, + SH5UTIL_MODE_ITEM_EXTRACT, } sh5util_mode_t; typedef struct { @@ -91,6 +88,7 @@ typedef struct { char *node; char *output; char *series; + char *data_item; int step_id; char *user; int verbose; @@ -110,16 +108,27 @@ sh5util [<OPTION>] -j <job[.stepid]> \n\ -E, --extract: \n\ Instead of merge node-step files (default) Extract data \n\ series from job file. \n\ - Extract mode options (all imply --extract) \n\ + Extract mode options \n\ -i, --input: merged file to extract from \n\ - (default ./job_$jobid.h5)\n\ + (default ./job_$jobid.h5) \n\ -N --node: Node name to extract (default is all) \n\ -l, --level: Level to which series is attached \n\ [Node:Totals|Node:TimeSeries] \n\ (default Node:Totals) \n\ - -s, --series: Name of series [Name|Tasks] \n\ - Name=Specific Name, 'Tasks' is all tasks \n\ + -s, --series: Name of series \n\ + Energy | Lustre | Network | Tasks | Task_# \n\ + 'Tasks' is all tasks, Task_# is task_id \n\ (default is everything) \n\ + -I, --item-extract: \n\ + Instead of merge node-step files (default) Extract data \n\ + item from one series from all samples on all nodes \n\ + from the job file. \n\ + Item-Extract mode options \n\ + -i, --input: merged file to extract from \n\ + (default ./job_$jobid.h5) \n\ + -s, --series: Name of series \n\ + Energy | Lustre | Network | Task \n\ + -d, --data: Name of data item in series (see man page) \n\ -j, --jobs: Format is <job(.step)>. Merge this job/step. \n\ or comma-separated list of job steps. This option is \n\ required. Not specifying a step will result in all \n\ @@ -159,6 +168,21 @@ static void _delete_string_list(char** list, int listLen) return; } +static void _remove_empty_outout() +{ + struct stat sb; + + if (stat(params.output, &sb) == -1) { + error("Failed to stat %s: %m",params.output); + exit(1); + } + if (sb.st_size == 0) { + error("No data in %s (it has been removed)", params.output); + remove(params.output); + } + +} + static void _init_opts() { memset(¶ms, 0, sizeof(sh5util_opts_t)); @@ -176,6 +200,8 @@ static void _set_options(const int argc, char **argv) static struct option long_options[] = { {"extract", no_argument, 0, 'E'}, + {"item-extract", no_argument, 0, 'I'}, + {"data", required_argument, 0, 'd'}, {"help", no_argument, 0, 'h'}, {"jobs", required_argument, 0, 'j'}, {"input", required_argument, 0, 'i'}, @@ -196,14 +222,21 @@ static void _set_options(const int argc, char **argv) _init_opts(); while (1) { /* now cycle through the command line */ - c = getopt_long(argc, argv, "Ehi:j:l:N:o:p:s:Su:vV", + c = getopt_long(argc, argv, "d:Ehi:Ij:l:N:o:p:s:Su:vV", long_options, &option_index); if (c == -1) break; switch (c) { + case 'd': + params.data_item = xstrdup(optarg); + params.data_item = xstrtolower(params.data_item); + break; case 'E': params.mode = SH5UTIL_MODE_EXTRACT; break; + case 'I': + params.mode = SH5UTIL_MODE_ITEM_EXTRACT; + break; case 'h': params.help = 1; break; @@ -229,6 +262,13 @@ static void _set_options(const int argc, char **argv) params.dir = xstrdup(optarg); break; case 's': + if (strcmp(optarg,GRP_ENERGY) + && strcmp(optarg,GRP_LUSTRE) + && strcmp(optarg,GRP_NETWORK) + && strncmp(optarg,GRP_TASK,strlen(GRP_TASK))) { + error("--series=\"%s\" invalid", optarg); + exit(1); + } params.series = xstrdup(optarg); break; case 'S': @@ -289,9 +329,10 @@ static void _set_options(const int argc, char **argv) /* FIXME : For now all these only work for extract. Seems * like it would be easy to add the logic to "merge" as well. */ - if (params.input || params.level || params.node - || (params.step_id != -1) || params.series) + if (params.level || params.node || params.series) params.mode = SH5UTIL_MODE_EXTRACT; + if (params.data_item) + params.mode = SH5UTIL_MODE_ITEM_EXTRACT; if (params.mode == SH5UTIL_MODE_EXTRACT) { if (!params.level) @@ -304,6 +345,23 @@ static void _set_options(const int argc, char **argv) "./extract_%d.csv", params.job_id); } + if (params.mode == SH5UTIL_MODE_ITEM_EXTRACT) { + if (!params.data_item) + fatal("You need to supply a --data name "); + + if (!params.series) + fatal("You need to supply a --series name "); + + if (!params.input) + params.input = xstrdup_printf( + "./job_%d.h5", params.job_id); + + if (!params.output) + params.output = xstrdup_printf("./%s_%s_%d.csv", + params.series, + params.data_item, + params.job_id); + } if (!params.output) params.output = xstrdup_printf("./job_%d.h5", params.job_id); @@ -1159,6 +1217,463 @@ static void _extract_data() } +/* ============================================================================ + * ============================================================================ + * Functions for data item extraction + * ============================================================================ + * ========================================================================= */ + +// Get the data_set for a node +static void* _get_series_data(hid_t jgid_node, char* series, + hdf5_api_ops_t **ops_p, int *nsmp) +{ + + hid_t gid_level, gid_series; + int size_data; + void *data; + uint32_t type; + char *data_type; + hdf5_api_ops_t* ops; + + *nsmp = 0; // Initialize return arguments. + *ops_p = NULL; + + // Navigate from the node group to the data set + gid_level = get_group(jgid_node, GRP_SAMPLES); + if (gid_level == -1) { + return NULL; + } + gid_series = get_group(gid_level, series); + if (gid_series < 0) { + // This is okay, may not have ran long enough for + // a sample (srun hostname) + H5Gclose(gid_level); + return NULL; + } + data_type = get_string_attribute(gid_series, ATTR_DATATYPE); + if (!data_type) { + H5Gclose(gid_series); + H5Gclose(gid_level); + debug("No datatype in %s", series); + return NULL; + } + // Invoke the data type operator to get the data set + type = acct_gather_profile_type_from_string(data_type); + xfree(data_type); + ops = profile_factory(type); + if (ops == NULL) { + H5Gclose(gid_series); + H5Gclose(gid_level); + debug("Failed to create operations for %s", + acct_gather_profile_type_to_string(type)); + return NULL; + } + data = get_hdf5_data(gid_series, type, series, &size_data); + if (data) { + *nsmp = (size_data / ops->dataset_size()); + *ops_p = ops; + } else { + xfree(ops); + } + H5Gclose(gid_series); + H5Gclose(gid_level); + return data; +} + +static void _series_analysis(FILE *fp, bool hd, int stepx, int nseries, + int nsmp, char **series_name, char **tod, double *et, + double **all_series, uint64_t *series_smp) +{ + double *mn_series; // Min Value, each sample + double *mx_series; // Max value, each sample + double *sum_series; // Total of all series, each sample + double *smp_series; // all samples for one node + uint64_t *mn_sx; // Index of series with minimum value + uint64_t *mx_sx; // Index of series with maximum value + uint64_t *series_in_smp; // Number of series in the sample + + int max_smpx = 0; + double max_smp_series = 0; + double ave_series; + int ix, isx; + mn_series = (double*) xmalloc(nsmp*sizeof(double)); + mx_series = (double*) xmalloc(nsmp*sizeof(double)); + sum_series = (double*) xmalloc(nsmp*sizeof(double)); + mn_sx = (uint64_t*) xmalloc(nsmp*sizeof(uint64_t)); + mx_sx = (uint64_t*) xmalloc(nsmp*sizeof(uint64_t)); + series_in_smp = (uint64_t*) xmalloc(nsmp*sizeof(uint64_t)); + for (ix=0; ix<nsmp; ix++) { + for (isx=0; isx<nseries; isx++) { + if (series_smp[isx]<nsmp && ix>=series_smp[isx]) + continue; + series_in_smp[ix]++; + smp_series = all_series[isx]; + if (smp_series) { + sum_series[ix] += smp_series[ix]; + if (mn_series[ix] == 0 + || smp_series[ix] < mn_series[ix]) { + mn_series[ix] = smp_series[ix]; + mn_sx[ix] = isx; + } + if (mx_series[ix] == 0 + || smp_series[ix] > mx_series[ix]) { + mx_series[ix] = smp_series[ix]; + mx_sx[ix] = isx; + } + } + } + } + + for (ix=0; ix<nsmp; ix++) { + if (sum_series[ix] > max_smp_series) { + max_smpx = ix; + max_smp_series = sum_series[ix]; + } + } + + ave_series = sum_series[max_smpx] / series_in_smp[max_smpx]; + printf(" Step %d Maximum accumlated %s Value (%f) occurred " + "at %s (Elapsed Time=%d) Ave Node %f\n\n", + stepx, params.data_item, max_smp_series, + tod[max_smpx], (int) et[max_smpx], ave_series); + + // Put data for step + if (!hd) { + fprintf(fp,"TOD,Et,JobId,StepId,Min Node,Min %s," + "Ave %s,Max Node,Max %s,Total %s," + "Num Nodes",params.data_item,params.data_item, + params.data_item,params.data_item); + for (isx=0; isx<nseries; isx++) { + fprintf(fp,",%s",series_name[isx]); + } + fprintf(fp,"\n"); + } + for (ix=0; ix<nsmp; ix++) { + fprintf(fp,"%s, %d",tod[ix], (int) et[ix]); + fprintf(fp,",%d,%d",params.job_id,stepx); + fprintf(fp,",%s,%f",series_name[mn_sx[ix]], + mn_series[ix]); + ave_series = sum_series[ix] / series_in_smp[ix]; + fprintf(fp,",%f",ave_series); + fprintf(fp,",%s,%f",series_name[mx_sx[ix]], + mx_series[ix]); + fprintf(fp,",%f",sum_series[ix]); + fprintf(fp,",%"PRIu64"",series_in_smp[ix]); + for (isx=0; isx<nseries; isx++) { + if (series_smp[isx]<nsmp && ix>=series_smp[isx]) { + fprintf(fp,",0.0"); + } else { + smp_series = all_series[isx]; + fprintf(fp,",%f",smp_series[ix]); + } + } + fprintf(fp,"\n"); + } + + xfree(mn_series); + xfree(mx_series); + xfree(sum_series); + xfree(mn_sx); + xfree(mx_sx); + +} + +static void _get_all_node_series(FILE *fp, bool hd, hid_t jgid_step, int stepx) +{ + char **tod = NULL; // Date time at each sample + char **node_name; // Node Names + double **all_series; // Pointers to all sampled for each node + double *et = NULL; // Elapsed time at each sample + uint64_t *series_smp; // Number of samples in this series + + hid_t jgid_nodes, jgid_node; + int nnodes, ndx, len, nsmp = 0, nitem = -1; + char jgrp_node_name[MAX_GROUP_NAME+1]; + void* series_data = NULL; + hdf5_api_ops_t* ops; + + nnodes = get_int_attribute(jgid_step, ATTR_NNODES); + // allocate node arrays + series_smp = (uint64_t*) xmalloc(nnodes*(sizeof(uint64_t))); + if (series_smp == NULL) + fatal("Failed to get memory for node_samples"); + node_name = (char**) xmalloc(nnodes*(sizeof(char*))); + if (node_name == NULL) + fatal("Failed to get memory for node_name"); + all_series = (double**) xmalloc(nnodes*(sizeof(double*))); + if (all_series == NULL) + fatal("Failed to get memory for all_series"); + jgid_nodes = get_group(jgid_step, GRP_NODES); + if (jgid_nodes < 0) + fatal("Failed to open group %s", GRP_NODES); + for (ndx=0; ndx<nnodes; ndx++) { + + len = H5Lget_name_by_idx(jgid_nodes, ".", H5_INDEX_NAME, + H5_ITER_INC, ndx, jgrp_node_name, + MAX_GROUP_NAME, H5P_DEFAULT); + if ((len < 0) || (len > MAX_GROUP_NAME)) { + debug("Invalid node name=%s", jgrp_node_name); + continue; + } + node_name[ndx] = xstrdup(jgrp_node_name); + jgid_node = get_group(jgid_nodes, jgrp_node_name); + if (jgid_node < 0) { + debug("Failed to open group %s", jgrp_node_name); + continue; + } + ops = NULL; + nitem = 0; + series_data = _get_series_data(jgid_node, params.series, + &ops, &nitem); + if (series_data==NULL || nitem==0 || ops==NULL) { + if (ops != NULL) + xfree(ops); + continue; + } + all_series[ndx] = ops->get_series_values( + params.data_item, series_data, nitem); + if (!all_series[ndx]) + fatal("No data item %s",params.data_item); + series_smp[ndx] = nitem; + if (ndx == 0) { + nsmp = nitem; + tod = ops->get_series_tod(series_data, nitem); + et = ops->get_series_values("time", + series_data, nitem); + } else { + if (nitem > nsmp) { + // new largest number of samples + _delete_string_list(tod, nsmp); + xfree(et); + nsmp = nitem; + tod = ops->get_series_tod(series_data, + nitem); + et = ops->get_series_values("time", + series_data, nitem); + } + } + xfree(ops); + xfree(series_data); + H5Gclose(jgid_node); + } + if (nsmp == 0) { + // May be bad series name + info("No values %s for series %s found in step %d", + params.data_item,params.series, + stepx); + } else { + _series_analysis(fp, hd, stepx, nnodes, nsmp, + node_name, tod, et, all_series, series_smp); + } + for (ndx=0; ndx<nnodes; ndx++) { + xfree(node_name[ndx]); + xfree(all_series[ndx]); + } + xfree(node_name); + xfree(all_series); + xfree(series_smp); + _delete_string_list(tod, nsmp); + xfree(et); + + H5Gclose(jgid_nodes); + +} + +static void _get_all_task_series(FILE *fp, bool hd, hid_t jgid_step, int stepx) +{ + + hid_t jgid_tasks, jgid_task, jgid_nodes, jgid_node; + H5G_info_t group_info; + int ntasks,itx, tid; + uint64_t *task_id; + char **task_node_name; // Node Name for each task + + char **tod = NULL; // Date time at each sample + char **series_name; // Node Names + double **all_series; // Pointers to all sampled for each node + double *et = NULL; // Elapsed time at each sample + uint64_t *series_smp; // Number of samples in this series + + int nnodes, ndx, len, nsmp = 0, nitem = -1; + char jgrp_node_name[MAX_GROUP_NAME+1]; + char jgrp_task_name[MAX_GROUP_NAME+1]; + char buf[MAX_GROUP_NAME+1]; + void* series_data = NULL; + hdf5_api_ops_t* ops; + + jgid_nodes = get_group(jgid_step, GRP_NODES); + if (jgid_nodes < 0) + fatal("Failed to open group %s", GRP_NODES); + jgid_tasks = get_group(jgid_step, GRP_TASKS); + if (jgid_tasks < 0) + fatal("No tasks in step %d", stepx); + H5Gget_info(jgid_tasks, &group_info); + ntasks = (int) group_info.nlinks; + if (ntasks <= 0) + fatal("No tasks in step %d", stepx); + task_id = xmalloc(ntasks*sizeof(uint64_t)); + if (task_id == NULL) + fatal("Failed to get memory for task_ids"); + task_node_name = xmalloc(ntasks*sizeof(char*)); + if (task_node_name == NULL) + fatal("Failed to get memory for task_node_names"); + + for (itx = 0; itx<ntasks; itx++) { + // Get the name of the group. + len = H5Lget_name_by_idx(jgid_tasks, ".", H5_INDEX_NAME, + H5_ITER_INC, itx, buf, MAX_GROUP_NAME, + H5P_DEFAULT); + if ((len > 0) && (len < MAX_GROUP_NAME)) { + jgid_task = H5Gopen(jgid_tasks, buf, H5P_DEFAULT); + if (jgid_task < 0) + fatal("Failed to open %s", buf); + } else + fatal("Illegal task name %s",buf); + task_id[itx] = get_int_attribute(jgid_task, ATTR_TASKID); + task_node_name[itx] = get_string_attribute(jgid_task, + ATTR_NODENAME); + H5Gclose(jgid_task); + } + H5Gclose(jgid_tasks); + + nnodes = get_int_attribute(jgid_step, ATTR_NNODES); + // allocate node arrays + series_smp = (uint64_t*) xmalloc(ntasks*(sizeof(uint64_t))); + if (series_smp == NULL) + fatal("Failed to get memory for node_samples"); + series_name = (char**) xmalloc(ntasks*(sizeof(char*))); + if (series_name == NULL) + fatal("Failed to get memory for series_name"); + all_series = (double**) xmalloc(ntasks*(sizeof(double*))); + if (all_series == NULL) + fatal("Failed to get memory for all_series"); + + for (ndx=0; ndx<nnodes; ndx++) { + + len = H5Lget_name_by_idx(jgid_nodes, ".", H5_INDEX_NAME, + H5_ITER_INC, ndx, jgrp_node_name, + MAX_GROUP_NAME, H5P_DEFAULT); + if ((len < 0) || (len > MAX_GROUP_NAME)) + fatal("Invalid node name=%s", jgrp_node_name); + jgid_node = get_group(jgid_nodes, jgrp_node_name); + + if (jgid_node < 0) + fatal("Failed to open group %s", jgrp_node_name); + for (itx = 0; itx<ntasks; itx++) { + if (strcmp(jgrp_node_name, task_node_name[itx]) != 0) + continue; + tid = task_id[itx]; + series_name[itx] = xstrdup_printf("%s_%d %s", + GRP_TASK,tid,jgrp_node_name); + sprintf(jgrp_task_name,"%s_%d",GRP_TASK, tid); + + ops = NULL; + nitem = 0; + series_data = _get_series_data(jgid_node, + jgrp_task_name, &ops, &nitem); + if (series_data==NULL || nitem==0 || ops==NULL) { + if (ops != NULL) + xfree(ops); + continue; + } + all_series[itx] = ops->get_series_values( + params.data_item, series_data, nitem); + if (!all_series[ndx]) + fatal("No data item %s",params.data_item); + series_smp[itx] = nitem; + if (nsmp == 0) { + nsmp = nitem; + tod = ops->get_series_tod(series_data, nitem); + et = ops->get_series_values("time", + series_data, nitem); + } else { + if (nitem > nsmp) { + // new largest number of samples + _delete_string_list(tod, nsmp); + xfree(et); + nsmp = nitem; + tod = ops->get_series_tod(series_data, + nitem); + et = ops->get_series_values("time", + series_data, nitem); + } + } + xfree(ops); + xfree(series_data); + } + H5Gclose(jgid_node); + } + if (nsmp == 0) { + // May be bad series name + info("No values %s for series %s found in step %d", + params.data_item,params.series, + stepx); + } else { + _series_analysis(fp, hd, stepx, ntasks, nsmp, + series_name, tod, et, all_series, series_smp); + } + for (itx=0; itx<ntasks; itx++) { + xfree(all_series[itx]); + } + xfree(series_name); + xfree(all_series); + xfree(series_smp); + _delete_string_list(tod, nsmp); + xfree(et); + _delete_string_list(task_node_name, ntasks); + + H5Gclose(jgid_nodes); +} + +static void _series_data() +{ + FILE *fp = NULL; + bool hd = false; + hid_t fid_job, jgid_root, jgid_step; + int nsteps, stepx; + char jgrp_step_name[MAX_GROUP_NAME+1]; + + fp = fopen(params.output, "w"); + if (fp == NULL) { + error("Failed to create output file %s -- %m", params.output); + return; + } + + fid_job = H5Fopen(params.input, H5F_ACC_RDONLY, H5P_DEFAULT); + if (fid_job < 0) { + fclose(fp); + error("Failed to open %s", params.input); + return; + } + jgid_root = H5Gopen(fid_job, "/", H5P_DEFAULT); + if (jgid_root < 0) { + fclose(fp); + H5Fclose(fid_job); + error("Failed to open root"); + return; + } + nsteps = get_int_attribute(jgid_root, ATTR_NSTEPS); + for (stepx=0; stepx<nsteps; stepx++) { + if ((params.step_id != -1) && (stepx != params.step_id)) + continue; + sprintf(jgrp_step_name, "%s_%d", GRP_STEP, stepx); + jgid_step = get_group(jgid_root, jgrp_step_name); + if (jgid_step < 0) { + fatal("Failed to open group %s", jgrp_step_name); + } + if (strncmp(params.series,GRP_TASK,strlen(GRP_TASK)) == 0) + _get_all_task_series(fp,hd,jgid_step, stepx); + else + _get_all_node_series(fp,hd,jgid_step, stepx); + hd = true; + H5Gclose(jgid_step); + } + + + H5Gclose(jgid_root); + H5Fclose(fid_job); + fclose(fp); +} int main (int argc, char **argv) { @@ -1180,10 +1695,17 @@ int main (int argc, char **argv) params.input, params.output); _extract_data(); break; + case SH5UTIL_MODE_ITEM_EXTRACT: + info("Extracting '%s' from '%s' data from %s into %s\n", + params.data_item, params.series, + params.input, params.output); + _series_data(); + break; default: error("Unknown type %d", params.mode); break; } + _remove_empty_outout(); profile_fini(); xfree(params.dir); xfree(params.node); -- GitLab