diff --git a/NEWS b/NEWS index 931281a8496283b0009e3986cd05dd59a5f08568..0333b3f6226b6540f652609d8099f89e212a1dba 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,10 @@ This file describes changes in recent versions of SLURM. It primarily documents those changes that are of interest to users and admins. +* Changes in SLURM 1.1.0-pre6 +============================= + -- Added logic to "stat" a running job with sacct option -S use -j to specify + job.step * Changes in SLURM 1.1.0-pre5 ============================= -- Added step completion RPC logic diff --git a/src/common/forward.c b/src/common/forward.c index cd4fddcd7fd0aaa3c6a1651272253f67716c03ac..de755a9e318af0baadb43afb5aae1d5e68773821 100644 --- a/src/common/forward.c +++ b/src/common/forward.c @@ -107,13 +107,16 @@ void *_forward_thread(void *arg) ret_data_info->node_name = xstrdup(fwd_msg->node_name); ret_data_info->nodeid = fwd_msg->header.srun_node_id; for(i=0; i<fwd_msg->header.forward.cnt; i++) { - strncpy(name, - &fwd_msg->header. - forward.name[i * MAX_SLURM_NAME], - MAX_SLURM_NAME); ret_data_info = xmalloc(sizeof(ret_data_info_t)); list_push(type->ret_data_list, ret_data_info); - ret_data_info->node_name = xstrdup(name); + if(fwd_msg->header.forward.name) { + strncpy(name, + &fwd_msg->header. + forward.name[i * MAX_SLURM_NAME], + MAX_SLURM_NAME); + ret_data_info->node_name = xstrdup(name); + } else + ret_data_info->node_name = NULL; ret_data_info->nodeid = fwd_msg->header.forward.node_id[i]; } @@ -267,10 +270,13 @@ extern int forward_msg(forward_struct_t *forward_struct, memcpy(&forward_msg->addr, &header->forward.addr[i], sizeof(slurm_addr)); - strncpy(forward_msg->node_name, - &header->forward.name[i * MAX_SLURM_NAME], - MAX_SLURM_NAME); - + if(header->forward.name) { + strncpy(forward_msg->node_name, + &header->forward.name[i * MAX_SLURM_NAME], + MAX_SLURM_NAME); + } else + memset(forward_msg->node_name, 0, MAX_SLURM_NAME); + forward_set(&forward_msg->header.forward, span[thr_count], &i, @@ -315,7 +321,12 @@ extern int forward_set(forward_t *forward, if(span > 0) { forward->addr = xmalloc(sizeof(slurm_addr) * span); - forward->name = xmalloc(sizeof(char) * (MAX_SLURM_NAME * span)); + if(from->name) + forward->name = xmalloc(sizeof(char) + * (MAX_SLURM_NAME * span)); + else + forward->name = NULL; + forward->node_id = xmalloc(sizeof(int32_t) * span); forward->timeout = from->timeout; forward->init = FORWARD_INIT; @@ -325,9 +336,10 @@ extern int forward_set(forward_t *forward, &from->addr[*pos+j], sizeof(slurm_addr)); //forward->addr[j-1] = forward_addr[*pos+j]; - strncpy(&forward->name[(j-1) * MAX_SLURM_NAME], - &from->name[(*pos+j) * MAX_SLURM_NAME], - MAX_SLURM_NAME); + if(from->name) + strncpy(&forward->name[(j-1) * MAX_SLURM_NAME], + &from->name[(*pos+j) * MAX_SLURM_NAME], + MAX_SLURM_NAME); if(from->node_id) forward->node_id[j-1] = from->node_id[*pos+j]; @@ -345,6 +357,7 @@ extern int forward_set(forward_t *forward, *pos += j; } else { forward_init(forward, NULL); + forward->timeout = from->timeout; } return SLURM_SUCCESS; @@ -385,8 +398,12 @@ extern int forward_set_launch(forward_t *forward, if(span > 0) { forward->addr = xmalloc(sizeof(slurm_addr) * span); - forward->name = - xmalloc(sizeof(char) * (MAX_SLURM_NAME * span)); + if(step_layout->host) + forward->name = + xmalloc(sizeof(char) + * (MAX_SLURM_NAME * span)); + else + forward->name = NULL; forward->node_id = xmalloc(sizeof(int32_t) * span); forward->timeout = timeout; forward->init = FORWARD_INIT; @@ -407,9 +424,10 @@ extern int forward_set_launch(forward_t *forward, &slurmd_addr[i], sizeof(slurm_addr)); //forward->addr[j-1] = slurmd_addr[i]; - strncpy(&forward->name[(j-1) * MAX_SLURM_NAME], - step_layout->host[*pos+j], - MAX_SLURM_NAME); + if(step_layout->host) + strncpy(&forward->name[(j-1) * MAX_SLURM_NAME], + step_layout->host[*pos+j], + MAX_SLURM_NAME); forward->node_id[j-1] = (*pos+j); /* strncpy(name, */ /* step_layout->host[*pos+j], */ @@ -423,6 +441,7 @@ extern int forward_set_launch(forward_t *forward, *pos += j; } else { forward_init(forward, NULL); + forward->timeout = timeout; } return SLURM_SUCCESS; @@ -446,11 +465,16 @@ extern int no_resp_forwards(forward_t *forward, List *ret_list, int err) type->err = err; type->ret_data_list = list_create(destroy_data_info); for(i=0; i<forward->cnt; i++) { - strncpy(name, - &forward->name[i * MAX_SLURM_NAME], MAX_SLURM_NAME); ret_data_info = xmalloc(sizeof(ret_data_info_t)); list_push(type->ret_data_list, ret_data_info); - ret_data_info->node_name = xstrdup(name); + if(forward->name) { + strncpy(name, + &forward->name[i * MAX_SLURM_NAME], + MAX_SLURM_NAME); + ret_data_info->node_name = xstrdup(name); + } else + ret_data_info->node_name = NULL; + ret_data_info->nodeid = forward->node_id[i]; } no_forward: diff --git a/src/common/slurm_jobacct.c b/src/common/slurm_jobacct.c index 759a98b33b39ab17567b7cee40b33b7354167d40..00c41c458eadd422c40cac75b7400c9ab33d8938 100644 --- a/src/common/slurm_jobacct.c +++ b/src/common/slurm_jobacct.c @@ -72,6 +72,8 @@ typedef struct slurm_jobacct_ops { void *data); void (*jobacct_aggregate) (jobacctinfo_t *dest, jobacctinfo_t *from); + void (*jobacct_2_sacct) (sacct_t *sacct, + jobacctinfo_t *jobacct); void (*jobacct_pack) (jobacctinfo_t *jobacct, Buf buffer); int (*jobacct_unpack) (jobacctinfo_t **jobacct, Buf buffer); int (*jobacct_init) (char *job_acct_log); @@ -173,6 +175,7 @@ _slurm_jobacct_get_ops( slurm_jobacct_context_t c ) "jobacct_p_setinfo", "jobacct_p_getinfo", "jobacct_p_aggregate", + "jobacct_p_2_sacct", "jobacct_p_pack", "jobacct_p_unpack", "jobacct_p_init_slurmctld", @@ -359,6 +362,18 @@ extern void jobacct_g_aggregate(jobacctinfo_t *dest, jobacctinfo_t *from) return; } +extern void jobacct_g_2_sacct(sacct_t *sacct, jobacctinfo_t *jobacct) +{ + if (_slurm_jobacct_init() < 0) + return; + + slurm_mutex_lock( &g_jobacct_context_lock ); + if ( g_jobacct_context ) + (*(g_jobacct_context->ops.jobacct_2_sacct))(sacct, jobacct); + slurm_mutex_unlock( &g_jobacct_context_lock ); + return; +} + extern void jobacct_g_pack(jobacctinfo_t *jobacct, Buf buffer) { if (_slurm_jobacct_init() < 0) diff --git a/src/common/slurm_jobacct.h b/src/common/slurm_jobacct.h index a04c6986c3e7a31d75ae35ad4e88e98e7a3fd205..eaff1b2934aa944fd5d0fbf24fcf885d9c5012a5 100644 --- a/src/common/slurm_jobacct.h +++ b/src/common/slurm_jobacct.h @@ -52,8 +52,9 @@ #include "src/slurmd/slurmstepd/slurmstepd_job.h" #include "src/slurmctld/slurmctld.h" +#include "src/sacct/sacct_stat.h" -typedef struct slurm_jobacct_context * slurm_jobacct_context_t; +typedef struct slurm_jobacct_context *slurm_jobacct_context_t; /* common */ extern int jobacct_g_init_struct(jobacctinfo_t *jobacct, uint16_t tid); @@ -64,6 +65,7 @@ extern int jobacct_g_setinfo(jobacctinfo_t *jobacct, extern int jobacct_g_getinfo(jobacctinfo_t *jobacct, enum jobacct_data_type type, void *data); extern void jobacct_g_aggregate(jobacctinfo_t *dest, jobacctinfo_t *from); +extern void jobacct_g_2_sacct(sacct_t *sacct, jobacctinfo_t *jobacct); extern void jobacct_g_pack(jobacctinfo_t *jobacct, Buf buffer); extern int jobacct_g_unpack(jobacctinfo_t **jobacct, Buf buffer); diff --git a/src/common/slurm_protocol_defs.c b/src/common/slurm_protocol_defs.c index e8d5180d58a5e6e841ca1626ce6ee6f7d43171e1..909fc5d9ded12f7278fb8fedbca2148096086a64 100644 --- a/src/common/slurm_protocol_defs.c +++ b/src/common/slurm_protocol_defs.c @@ -939,3 +939,11 @@ extern void slurm_free_step_complete_msg(step_complete_msg_t *msg) xfree(msg); } } + +extern void slurm_free_stat_jobacct_msg(stat_jobacct_msg_t *msg) +{ + if (msg) { + jobacct_g_free(msg->jobacct); + xfree(msg); + } +} diff --git a/src/common/slurm_protocol_defs.h b/src/common/slurm_protocol_defs.h index b52b70ed4f3495b09dd6c7e178d50eb5d9b9b0d7..fbb2b2d63d9ad6c87eb21280e91393be965b1522 100644 --- a/src/common/slurm_protocol_defs.h +++ b/src/common/slurm_protocol_defs.h @@ -144,7 +144,8 @@ typedef enum { REQUEST_STEP_COMPLETE, REQUEST_COMPLETE_JOB_ALLOCATION, REQUEST_COMPLETE_BATCH_SCRIPT, - + MESSAGE_STAT_JOBACCT, + REQUEST_LAUNCH_TASKS = 6001, RESPONSE_LAUNCH_TASKS, MESSAGE_TASK_EXIT, @@ -324,6 +325,13 @@ typedef struct step_complete_msg { jobacctinfo_t *jobacct; } step_complete_msg_t; +typedef struct stat_jobacct_msg { + uint32_t job_id; + uint32_t step_id; + uint32_t num_tasks; + jobacctinfo_t *jobacct; +} stat_jobacct_msg_t; + typedef struct kill_tasks_msg { uint32_t job_id; uint32_t job_step_id; @@ -674,6 +682,7 @@ void slurm_free_partition_info_msg(partition_info_msg_t * msg); void slurm_free_get_kvs_msg(kvs_get_msg_t *msg); void inline slurm_free_file_bcast_msg(file_bcast_msg_t *msg); void inline slurm_free_step_complete_msg(step_complete_msg_t *msg); +void inline slurm_free_stat_jobacct_msg(stat_jobacct_msg_t *msg); extern char *job_reason_string(enum job_wait_reason inx); extern char *job_state_string(enum job_states inx); diff --git a/src/common/slurm_protocol_pack.c b/src/common/slurm_protocol_pack.c index d2d6637b0ed3d0bab7f7d4171c14372b41321839..c0532e2a86b4355fd0139463cec3ec0d534f3c72 100644 --- a/src/common/slurm_protocol_pack.c +++ b/src/common/slurm_protocol_pack.c @@ -206,6 +206,10 @@ static void _pack_complete_batch_script_msg( complete_batch_script_msg_t * msg, Buf buffer); static int _unpack_complete_batch_script_msg( complete_batch_script_msg_t ** msg_ptr, Buf buffer); + +static void _pack_stat_jobacct_msg(stat_jobacct_msg_t * msg, Buf buffer); +static int _unpack_stat_jobacct_msg(stat_jobacct_msg_t ** msg_ptr, Buf buffer); + static void _pack_step_complete_msg(step_complete_msg_t * msg, Buf buffer); static int _unpack_step_complete_msg(step_complete_msg_t ** @@ -516,6 +520,10 @@ pack_msg(slurm_msg_t const *msg, Buf buffer) _pack_step_complete_msg((step_complete_msg_t *)msg->data, buffer); break; + case MESSAGE_STAT_JOBACCT: + _pack_stat_jobacct_msg((stat_jobacct_msg_t *) msg->data, + buffer); + break; case REQUEST_SIGNAL_JOB: _pack_signal_job_msg((signal_job_msg_t *) msg->data, buffer); break; @@ -789,6 +797,10 @@ unpack_msg(slurm_msg_t * msg, Buf buffer) **) & (msg->data), buffer); break; + case MESSAGE_STAT_JOBACCT: + rc = _unpack_stat_jobacct_msg( + (stat_jobacct_msg_t **) &(msg->data), buffer); + break; case REQUEST_SIGNAL_JOB: rc = _unpack_signal_job_msg((signal_job_msg_t **)&(msg->data), buffer); @@ -2797,6 +2809,38 @@ _unpack_complete_batch_script_msg( return SLURM_ERROR; } +static void +_pack_stat_jobacct_msg(stat_jobacct_msg_t * msg, Buf buffer) +{ + pack32((uint32_t)msg->job_id, buffer); + pack32((uint32_t)msg->step_id, buffer); + pack32((uint32_t)msg->num_tasks, buffer); + jobacct_g_pack(msg->jobacct, buffer); +} + + +static int +_unpack_stat_jobacct_msg(stat_jobacct_msg_t ** msg_ptr, Buf buffer) +{ + stat_jobacct_msg_t *msg; + + msg = xmalloc(sizeof(stat_jobacct_msg_t)); + *msg_ptr = msg; + + safe_unpack32(&msg->job_id, buffer); + safe_unpack32(&msg->step_id, buffer); + safe_unpack32(&msg->num_tasks, buffer); + jobacct_g_unpack(&msg->jobacct, buffer); + + return SLURM_SUCCESS; + +unpack_error: + xfree(msg); + *msg_ptr = NULL; + return SLURM_ERROR; + +} + static void _pack_step_complete_msg(step_complete_msg_t * msg, Buf buffer) { diff --git a/src/plugins/jobacct/aix/jobacct_aix.c b/src/plugins/jobacct/aix/jobacct_aix.c index f1b17ae5086ef9ba476a11c4ebe01ad5822acb15..f3269d70bf1cb5e7957a5c8ed820d872d08831be 100644 --- a/src/plugins/jobacct/aix/jobacct_aix.c +++ b/src/plugins/jobacct/aix/jobacct_aix.c @@ -121,6 +121,11 @@ void jobacct_p_aggregate(struct jobacctinfo *dest, struct jobacctinfo *from) common_aggregate(dest, from); } +void jobacct_p_2_sacct(sacct_t *sacct, struct jobacctinfo *jobacct) +{ + common_2_sacct(sacct, jobacct); +} + void jobacct_p_pack(struct jobacctinfo *jobacct, Buf buffer) { common_pack(jobacct, buffer); diff --git a/src/plugins/jobacct/bluegene/jobacct_bluegene.c b/src/plugins/jobacct/bluegene/jobacct_bluegene.c index 2350ff99a86674619253a770167bc163612d1b12..3705f778d80e7943ae71da9d23be6051a73ef1d9 100644 --- a/src/plugins/jobacct/bluegene/jobacct_bluegene.c +++ b/src/plugins/jobacct/bluegene/jobacct_bluegene.c @@ -124,6 +124,11 @@ void jobacct_p_aggregate(struct jobacctinfo *dest, struct jobacctinfo *from) common_aggregate(dest, from); } +void jobacct_p_2_sacct(sacct_t *sacct, struct jobacctinfo *jobacct) +{ + common_2_sacct(sacct, jobacct); +} + void jobacct_p_pack(struct jobacctinfo *jobacct, Buf buffer) { common_pack(jobacct, buffer); diff --git a/src/plugins/jobacct/common/common_slurmstepd.c b/src/plugins/jobacct/common/common_slurmstepd.c index 62184d75392dcaa9c5da41fe02c4d3001bb1a7d1..afcca62925dc658c95d8ffcc1d36f9f92f2e6896 100644 --- a/src/plugins/jobacct/common/common_slurmstepd.c +++ b/src/plugins/jobacct/common/common_slurmstepd.c @@ -78,12 +78,10 @@ extern struct jobacctinfo *common_stat_task(pid_t pid) if(jobacct->pid == pid) break; } - slurm_mutex_unlock(&jobacct_lock); list_iterator_destroy(itr); - - return jobacct; + slurm_mutex_unlock(&jobacct_lock); error: - return NULL; + return jobacct; } extern int common_remove_task(pid_t pid) diff --git a/src/plugins/jobacct/common/jobacct_common.c b/src/plugins/jobacct/common/jobacct_common.c index af562e86f52a68b4c39041dcfb1cde32128d91f1..1b73f38d6822bcb2d15b40a7dd3c8ac2c6038c6b 100644 --- a/src/plugins/jobacct/common/jobacct_common.c +++ b/src/plugins/jobacct/common/jobacct_common.c @@ -220,6 +220,9 @@ rwfail: extern void common_aggregate(struct jobacctinfo *dest, struct jobacctinfo *from) { + xassert(dest); + xassert(from); + slurm_mutex_lock(&jobacct_lock); if(dest->max_vsize < from->max_vsize) { dest->max_vsize = from->max_vsize; @@ -291,8 +294,37 @@ extern void common_aggregate(struct jobacctinfo *dest, slurm_mutex_unlock(&jobacct_lock); } +extern void common_2_sacct(sacct_t *sacct, struct jobacctinfo *jobacct) +{ + xassert(jobacct); + xassert(sacct); + slurm_mutex_lock(&jobacct_lock); + sacct->max_vsize = jobacct->max_vsize; + sacct->max_vsize_task = jobacct->max_vsize_task; + sacct->ave_vsize = jobacct->tot_vsize; + sacct->max_rss = jobacct->max_rss; + sacct->max_rss_task = jobacct->max_rss_task; + sacct->ave_rss = jobacct->tot_rss; + sacct->max_pages = jobacct->max_pages; + sacct->max_pages_task = jobacct->max_pages_task; + sacct->ave_pages = jobacct->tot_pages; + sacct->min_cpu = jobacct->min_cpu; + sacct->min_cpu_task = jobacct->min_cpu_task; + sacct->ave_cpu = jobacct->tot_cpu; + slurm_mutex_unlock(&jobacct_lock); +} + extern void common_pack(struct jobacctinfo *jobacct, Buf buffer) { + int i=0; + + if(!jobacct) { + for(i=0; i<26; i++) + pack32((uint32_t) 0, buffer); + for(i=0; i<4; i++) + pack16((uint16_t) 0, buffer); + return; + } slurm_mutex_lock(&jobacct_lock); pack32((uint32_t)jobacct->rusage.ru_utime.tv_sec, buffer); pack32((uint32_t)jobacct->rusage.ru_utime.tv_usec, buffer); @@ -313,17 +345,17 @@ extern void common_pack(struct jobacctinfo *jobacct, Buf buffer) pack32((uint32_t)jobacct->rusage.ru_nvcsw, buffer); pack32((uint32_t)jobacct->rusage.ru_nivcsw, buffer); pack32((uint32_t)jobacct->max_vsize, buffer); - pack16((uint16_t)jobacct->max_vsize_task, buffer); pack32((uint32_t)jobacct->tot_vsize, buffer); pack32((uint32_t)jobacct->max_rss, buffer); - pack16((uint16_t)jobacct->max_rss_task, buffer); pack32((uint32_t)jobacct->tot_rss, buffer); pack32((uint32_t)jobacct->max_pages, buffer); - pack16((uint16_t)jobacct->max_pages_task, buffer); pack32((uint32_t)jobacct->tot_pages, buffer); pack32((uint32_t)jobacct->min_cpu, buffer); - pack16((uint16_t)jobacct->min_cpu_task, buffer); pack32((uint32_t)jobacct->tot_cpu, buffer); + pack16((uint16_t)jobacct->max_vsize_task, buffer); + pack16((uint16_t)jobacct->max_rss_task, buffer); + pack16((uint16_t)jobacct->max_pages_task, buffer); + pack16((uint16_t)jobacct->min_cpu_task, buffer); slurm_mutex_unlock(&jobacct_lock); } @@ -369,17 +401,17 @@ extern int common_unpack(struct jobacctinfo **jobacct, Buf buffer) safe_unpack32(&uint32_tmp, buffer); (*jobacct)->rusage.ru_nivcsw = uint32_tmp; safe_unpack32(&(*jobacct)->max_vsize, buffer); - safe_unpack16(&(*jobacct)->max_vsize_task, buffer); safe_unpack32(&(*jobacct)->tot_vsize, buffer); safe_unpack32(&(*jobacct)->max_rss, buffer); - safe_unpack16(&(*jobacct)->max_rss_task, buffer); safe_unpack32(&(*jobacct)->tot_rss, buffer); safe_unpack32(&(*jobacct)->max_pages, buffer); - safe_unpack16(&(*jobacct)->max_pages_task, buffer); safe_unpack32(&(*jobacct)->tot_pages, buffer); safe_unpack32(&(*jobacct)->min_cpu, buffer); - safe_unpack16(&(*jobacct)->min_cpu_task, buffer); safe_unpack32(&(*jobacct)->tot_cpu, buffer); + safe_unpack16(&(*jobacct)->max_vsize_task, buffer); + safe_unpack16(&(*jobacct)->max_rss_task, buffer); + safe_unpack16(&(*jobacct)->max_pages_task, buffer); + safe_unpack16(&(*jobacct)->min_cpu_task, buffer); return SLURM_SUCCESS; unpack_error: diff --git a/src/plugins/jobacct/common/jobacct_common.h b/src/plugins/jobacct/common/jobacct_common.h index c23b58b7d2d350620fa3b1554b310d03c56725eb..4c738607140d1b42c05554a9f61283bbe29db45b 100644 --- a/src/plugins/jobacct/common/jobacct_common.h +++ b/src/plugins/jobacct/common/jobacct_common.h @@ -49,6 +49,7 @@ #include "src/common/list.h" #include "src/common/xstring.h" #include "src/common/node_select.h" + #include <ctype.h> #define BUFFER_SIZE 4096 @@ -91,6 +92,7 @@ extern int common_getinfo(struct jobacctinfo *jobacct, enum jobacct_data_type type, void *data); extern void common_aggregate(struct jobacctinfo *dest, struct jobacctinfo *from); +extern void common_2_sacct(sacct_t *sacct, struct jobacctinfo *jobacct); extern void common_pack(struct jobacctinfo *jobacct, Buf buffer); extern int common_unpack(struct jobacctinfo **jobacct, Buf buffer); diff --git a/src/plugins/jobacct/linux/jobacct_linux.c b/src/plugins/jobacct/linux/jobacct_linux.c index 3072648a9b41ed355b8cea3f2b0e033a092486a4..f4c69c9d9781242e335e43854ee70c64e551c262 100644 --- a/src/plugins/jobacct/linux/jobacct_linux.c +++ b/src/plugins/jobacct/linux/jobacct_linux.c @@ -123,6 +123,11 @@ void jobacct_p_aggregate(struct jobacctinfo *dest, struct jobacctinfo *from) common_aggregate(dest, from); } +void jobacct_p_2_sacct(sacct_t *sacct, struct jobacctinfo *jobacct) +{ + common_2_sacct(sacct, jobacct); +} + void jobacct_p_pack(struct jobacctinfo *jobacct, Buf buffer) { common_pack(jobacct, buffer); @@ -231,6 +236,7 @@ int jobacct_p_add_task(pid_t pid, uint16_t tid) struct jobacctinfo *jobacct_p_stat_task(pid_t pid) { + _get_process_data(); return common_stat_task(pid); } diff --git a/src/plugins/jobacct/none/jobacct_none.c b/src/plugins/jobacct/none/jobacct_none.c index b3c16025186df7835f1842ff58ec058555d8e934..2f6846da8623d333cc2cb8afbcf4484cd67f1144 100644 --- a/src/plugins/jobacct/none/jobacct_none.c +++ b/src/plugins/jobacct/none/jobacct_none.c @@ -120,6 +120,11 @@ void jobacct_p_aggregate(struct jobacctinfo *dest, struct jobacctinfo *from) return; } +void jobacct_p_2_sacct(sacct_t *sacct, struct jobacctinfo *jobacct) +{ + return; +} + void jobacct_p_pack(struct jobacctinfo *jobacct, Buf buffer) { return; diff --git a/src/sacct/Makefile.am b/src/sacct/Makefile.am index 0866bd7433631972b535e3c1aa61186219761edc..c5e5579c059498335865210fdc3b48e480379057 100644 --- a/src/sacct/Makefile.am +++ b/src/sacct/Makefile.am @@ -12,7 +12,7 @@ sacct_LDADD = \ $(top_builddir)/src/api/libslurm.la noinst_HEADERS = sacct.c -sacct_SOURCES = sacct.c process.c print.c options.c +sacct_SOURCES = sacct.c process.c print.c options.c sacct_stat.c force: $(sacct_LDADD) : force diff --git a/src/sacct/options.c b/src/sacct/options.c index 2cd3a62f0c2c8b1b1aa3c61dc406c9b38f13e088..ea30561b78fb92636d9970ff403b529143ee5441 100644 --- a/src/sacct/options.c +++ b/src/sacct/options.c @@ -204,13 +204,13 @@ void _help_msg(void) " accounting log file to refer to different jobs; such jobs\n" " can be distinguished by the \"job_start\" time stamp in the\n" " data records.\n" - " When data for specific jobs or jobsteps are requested with\n" - " the --jobs or --jobsteps options, we assume that the user\n" + " When data for specific jobs are requested with\n" + " the --jobs option, we assume that the user\n" " wants to see only the most recent job with that number. This\n" " behavior can be overridden by specifying --duplicates, in\n" " which case all records that match the selection criteria\n" " will be returned.\n" - " When neither --jobs or --jobsteps is specified, we report\n" + " When --jobs is not specified, we report\n" " data for all jobs that match the selection criteria, even if\n" " some of the job numbers are reused. Specify that you only\n" " want the most recent job for each selected job number with\n" @@ -245,11 +245,10 @@ void _help_msg(void) "--help-fields\n" " Print a list of fields that can be specified with the\n" " \"--fields\" option\n" - "-j <job_list>, --jobs=<job_list>\n" + "-j <job(.step)>, --jobs=<job(.step)>\n" " Display information about this job or comma-separated\n" - " list of jobs. The default is all jobs.\n" - "-J <job.step>, --jobstep=<job.step>\n" - " Show data only for the specified step of the specified job.\n" + " list of jobs. The default is all jobs. Adding .step will\n" + " display the specfic job step of that job.\n" "--noduplicates\n" " See the discussion under --duplicates.\n" "--noheader\n" @@ -291,6 +290,7 @@ void _init_params() params.opt_dump = 0; /* --dump */ params.opt_dup = -1; /* --duplicates; +1 = explicitly set */ params.opt_fdump = 0; /* --formattted_dump */ + params.opt_stat = 0; /* --stat */ params.opt_gid = -1; /* --gid (-1=wildcard, 0=root) */ params.opt_header = 1; /* can only be cleared */ params.opt_help = 0; /* --help */ @@ -304,7 +304,6 @@ void _init_params() params.opt_field_list = NULL; /* --fields= */ params.opt_filein = NULL; /* --file */ params.opt_job_list = NULL; /* --jobs */ - params.opt_jobstep_list = NULL; /* --jobstep */ params.opt_partition_list = NULL;/* --partitions */ params.opt_state_list = NULL; /* --states */ } @@ -416,6 +415,7 @@ int get_data(void) selected_step_t *selected_step = NULL; char *selected_part = NULL; ListIterator itr = NULL; + int show_full = 0; fd = _open_log_file(); @@ -448,11 +448,14 @@ int get_data(void) while((selected_step = list_next(itr))) { if (strcmp(selected_step->job, f[F_JOB])) continue; - /* job matches; does the step> */ - if (selected_step->step == NULL - || rec_type == JOB_STEP - || !strcmp(f[F_JOBSTEP], - selected_step->step)) { + /* job matches; does the step? */ + if(selected_step->step == NULL) { + show_full = 1; + list_iterator_destroy(itr); + goto foundjob; + } else if (rec_type == JOB_STEP + || !strcmp(f[F_JOBSTEP], + selected_step->step)) { list_iterator_destroy(itr); goto foundjob; } @@ -487,28 +490,28 @@ int get_data(void) printf("Bad data on a Job Start\n"); _show_rec(f); } else - process_start(f, lc); + process_start(f, lc, show_full); break; case JOB_STEP: if(i < JOB_STEP_LENGTH) { printf("Bad data on a Step entry\n"); _show_rec(f); } else - process_step(f, lc); + process_step(f, lc, show_full); break; case JOB_SUSPEND: if(i < JOB_TERM_LENGTH) { printf("Bad data on a Suspend entry\n"); _show_rec(f); } else - process_suspend(f, lc); + process_suspend(f, lc, show_full); break; case JOB_TERMINATED: if(i < JOB_TERM_LENGTH) { printf("Bad data on a Job Term\n"); _show_rec(f); } else - process_terminated(f, lc); + process_terminated(f, lc, show_full); break; default: if (params.opt_verbose > 1) @@ -536,10 +539,12 @@ void parse_command_line(int argc, char **argv) { extern int optind; int c, i, optionIndex = 0; - char *end, *start, *acct_type; + char *end = NULL, *start = NULL, *acct_type = NULL; selected_step_t *selected_step = NULL; ListIterator itr = NULL; struct stat stat_buf; + char *dot = NULL; + static struct option long_options[] = { {"all", 0,0, 'a'}, {"brief", 0, 0, 'b'}, @@ -549,12 +554,12 @@ void parse_command_line(int argc, char **argv) {"fields", 1, 0, 'F'}, {"file", 1, 0, 'f'}, {"formatted_dump", 0, 0, 'O'}, + {"stat", 0, 0, 'S'}, {"gid", 1, 0, 'g'}, {"group", 1, 0, 'g'}, {"help", 0, ¶ms.opt_help, 1}, {"help-fields", 0, ¶ms.opt_help, 2}, {"jobs", 1, 0, 'j'}, - {"jobstep", 1, 0, 'J'}, {"long", 0, 0, 'l'}, {"big_logfile", 0, ¶ms.opt_lowmem, 1}, {"noduplicates", 0, ¶ms.opt_dup, 0}, @@ -578,7 +583,7 @@ void parse_command_line(int argc, char **argv) opterr = 1; /* Let getopt report problems to the user */ while (1) { /* now cycle through the command line */ - c = getopt_long(argc, argv, "abde:F:f:g:hj:J:lOPp:s:tUu:Vv", + c = getopt_long(argc, argv, "abde:F:f:g:hj:J:lOPp:s:StUu:Vv", long_options, &optionIndex); if (c == -1) break; @@ -683,7 +688,9 @@ void parse_command_line(int argc, char **argv) break; case 'j': - if (strspn(optarg, "0123456789, ") < strlen(optarg)) { + if ((strspn(optarg, "0123456789, ") < strlen(optarg)) + && (strspn(optarg, ".0123456789, ") + < strlen(optarg))) { fprintf(stderr, "Invalid jobs list: %s\n", optarg); exit(1); @@ -697,21 +704,6 @@ void parse_command_line(int argc, char **argv) strcat(params.opt_job_list, ","); break; - case 'J': - if (strspn(optarg, ".0123456789, ") < strlen(optarg)) { - fprintf(stderr, "Invalid jobstep list: %s\n", - optarg); - exit(1); - } - params.opt_jobstep_list = - xrealloc(params.opt_jobstep_list, - (params.opt_jobstep_list==NULL? 0 : - strlen(params.opt_jobstep_list)) + - strlen(optarg) + 1); - strcat(params.opt_jobstep_list, optarg); - strcat(params.opt_jobstep_list, ","); - break; - case 'l': params.opt_field_list = xrealloc(params.opt_field_list, @@ -750,6 +742,10 @@ void parse_command_line(int argc, char **argv) strcat(params.opt_state_list, ","); break; + case 'S': + params.opt_stat = 1; + break; + case 't': params.opt_total = 1; break; @@ -805,7 +801,7 @@ void parse_command_line(int argc, char **argv) /* Now set params.opt_dup, unless they've already done so */ if (params.opt_dup < 0) /* not already set explicitly */ - if (params.opt_job_list || params.opt_jobstep_list) + if (params.opt_job_list) /* They probably want the most recent job N if * they requested specific jobs or steps. */ params.opt_dup = 0; @@ -816,12 +812,12 @@ void parse_command_line(int argc, char **argv) "\topt_dup=%d\n" "\topt_expire=%s (%lu seconds)\n" "\topt_fdump=%d\n" + "\topt_stat=%d\n" "\topt_field_list=%s\n" "\topt_filein=%s\n" "\topt_header=%d\n" "\topt_help=%d\n" "\topt_job_list=%s\n" - "\topt_jobstep_list=%s\n" "\topt_long=%d\n" "\topt_lowmem=%d\n" "\topt_partition_list=%s\n" @@ -834,12 +830,12 @@ void parse_command_line(int argc, char **argv) params.opt_dup, params.opt_expire_timespec, params.opt_expire, params.opt_fdump, + params.opt_stat, params.opt_field_list, params.opt_filein, params.opt_header, params.opt_help, params.opt_job_list, - params.opt_jobstep_list, params.opt_long, params.opt_lowmem, params.opt_partition_list, @@ -880,61 +876,39 @@ void parse_command_line(int argc, char **argv) } } - /* specific jobsteps requested? */ - if (params.opt_jobstep_list) { - char *dot; - - start = params.opt_jobstep_list; - while ((end = strstr(start, ","))) { - *end = 0;; - while (isspace(*start)) - start++; /* discard whitespace */ - dot = strstr(start, "."); - if (dot == NULL) { - fprintf(stderr, "Invalid jobstep: %s\n", - start); - exit(1); - } - *dot++ = 0; - selected_step = xmalloc(sizeof(selected_step_t)); - list_append(selected_steps, selected_step); - - selected_step->job = xstrdup(start); - selected_step->step = xstrdup(dot); - start = end + 1; - } - if (params.opt_verbose) { - fprintf(stderr, "Job steps requested:\n"); - itr = list_iterator_create(selected_steps); - while((selected_step = list_next(itr))) - fprintf(stderr, "\t: %s.%s\n", - selected_step->job, - selected_step->step); - list_iterator_destroy(itr); - - } - } - /* specific jobs requested? */ if (params.opt_job_list) { start = params.opt_job_list; while ((end = strstr(start, ","))) { + *end = 0; while (isspace(*start)) start++; /* discard whitespace */ - *end = 0; selected_step = xmalloc(sizeof(selected_step_t)); list_append(selected_steps, selected_step); selected_step->job = xstrdup(start); - selected_step->step = NULL; + dot = strstr(start, "."); + if (dot == NULL) { + debug2("No jobstep requested"); + selected_step->step = NULL; + } else { + *dot++ = 0; + selected_step->step = xstrdup(dot); + } start = end + 1; } if (params.opt_verbose) { fprintf(stderr, "Jobs requested:\n"); itr = list_iterator_create(selected_steps); - while((selected_step = list_next(itr))) - fprintf(stderr, "\t: %s\n", - selected_step->job); + while((selected_step = list_next(itr))) { + if(selected_step->step) + fprintf(stderr, "\t: %s.%s\n", + selected_step->job, + selected_step->step); + else + fprintf(stderr, "\t: %s\n", + selected_step->job); + } list_iterator_destroy(itr); } } @@ -1008,6 +982,7 @@ void do_dump(void) ListIterator itr_step = NULL; job_rec_t *job = NULL; step_rec_t *step = NULL; + float tempf; itr = list_iterator_create(jobs); while((job = list_next(itr))) { @@ -1026,8 +1001,20 @@ void do_dump(void) continue; if(job->sacct.min_cpu == NO_VAL) job->sacct.min_cpu = 0; + + if(list_count(job->steps)) { + tempf = job->sacct.ave_cpu/list_count(job->steps); + job->sacct.ave_cpu = (uint32_t)tempf; + tempf = job->sacct.ave_rss/list_count(job->steps); + job->sacct.ave_rss = (uint32_t)tempf; + tempf = job->sacct.ave_vsize/list_count(job->steps); + job->sacct.ave_vsize = (uint32_t)tempf; + tempf = job->sacct.ave_pages/list_count(job->steps); + job->sacct.ave_pages = (uint32_t)tempf; + } + /* JOB_START */ - if (params.opt_jobstep_list == NULL) { + if (job->show_full) { if (!job->job_start_seen && job->job_step_seen) { /* If we only saw JOB_TERMINATED, the * job was probably canceled. */ @@ -1103,7 +1090,7 @@ void do_dump(void) } list_iterator_destroy(itr_step); /* JOB_TERMINATED */ - if (params.opt_jobstep_list == NULL) { + if (job->show_full) { _dump_header(job->header); printf("JOB_TERMINATED %d ", job->elapsed); @@ -1603,19 +1590,18 @@ void do_help(void) */ void do_list(void) { - int do_jobs=1, - do_jobsteps=1; + int do_jobsteps = 1; int rc = 0; ListIterator itr = NULL; ListIterator itr_step = NULL; job_rec_t *job = NULL; step_rec_t *step = NULL; + float tempf; if (params.opt_total) do_jobsteps = 0; - else if (params.opt_jobstep_list) - do_jobs = 0; + itr = list_iterator_create(jobs); while((job = list_next(itr))) { if (!params.opt_dup) @@ -1660,7 +1646,19 @@ void do_list(void) continue; if(job->sacct.min_cpu == NO_VAL) job->sacct.min_cpu = 0; - if (do_jobs) { + + if(list_count(job->steps)) { + tempf = job->sacct.ave_cpu/list_count(job->steps); + job->sacct.ave_cpu = (uint32_t)tempf; + tempf = job->sacct.ave_rss/list_count(job->steps); + job->sacct.ave_rss = (uint32_t)tempf; + tempf = job->sacct.ave_vsize/list_count(job->steps); + job->sacct.ave_vsize = (uint32_t)tempf; + tempf = job->sacct.ave_pages/list_count(job->steps); + job->sacct.ave_pages = (uint32_t)tempf; + } + + if (job->show_full) { if (params.opt_state_list) { if(!selected_status[job->status]) continue; @@ -1686,6 +1684,24 @@ void do_list(void) list_iterator_destroy(itr); } +void do_stat() +{ + ListIterator itr = NULL; + uint32_t jobid = 0; + uint32_t stepid = 0; + selected_step_t *selected_step = NULL; + + itr = list_iterator_create(selected_steps); + while((selected_step = list_next(itr))) { + jobid = atoi(selected_step->job); + if(selected_step->step) + stepid = atoi(selected_step->step); + else + stepid = 0; + sacct_stat(jobid, stepid); + } + list_iterator_destroy(itr); +} void sacct_init() { int i=0; diff --git a/src/sacct/print.c b/src/sacct/print.c index 40d4bec6ebafc013acf1a800ab7383ddb734748f..4605b349f2c9f08a144d4ccf8e9977b3fb886dc2 100644 --- a/src/sacct/print.c +++ b/src/sacct/print.c @@ -317,14 +317,18 @@ void print_name(type_t type, void *object) printf("%-18s", "------------------"); break; case JOB: - if(strlen(job->jobname)<19) + if(!job->jobname) + printf("%-18s", "unknown"); + else if(strlen(job->jobname)<19) printf("%-18s", job->jobname); else printf("%-15.15s...", job->jobname); break; case JOBSTEP: - if(strlen(step->stepname)<19) + if(!step->stepname) + printf("%-18s", "unknown"); + else if(strlen(step->stepname)<19) printf("%-18s", step->stepname); else printf("%-15.15s...", step->stepname); @@ -622,14 +626,18 @@ void print_partition(type_t type, void *object) printf("%-10s", "----------"); break; case JOB: - if(strlen(job->header.partition)<11) + if(!job->header.partition) + printf("%-10s", "unknown"); + else if(strlen(job->header.partition)<11) printf("%-10s", job->header.partition); else printf("%-7.7s...", job->header.partition); break; case JOBSTEP: - if(strlen(step->header.partition)<11) + if(!step->header.partition) + printf("%-10s", "unknown"); + else if(strlen(step->header.partition)<11) printf("%-10s", step->header.partition); else printf("%-7.7s...", step->header.partition); @@ -651,14 +659,18 @@ void print_blockid(type_t type, void *object) printf("%-16s", "----------------"); break; case JOB: - if(strlen(job->header.partition)<17) - printf("%-16s", job->header.partition); + if(!job->header.blockid) + printf("%-16s", "unknown"); + else if(strlen(job->header.blockid)<17) + printf("%-16s", job->header.blockid); else - printf("%-13.13s...", job->header.partition); + printf("%-13.13s...", job->header.blockid); break; case JOBSTEP: - if(strlen(step->header.partition)<17) + if(!step->header.blockid) + printf("%-16s", "unknown"); + else if(strlen(step->header.blockid)<17) printf("%-16s", step->header.blockid); else printf("%-13.13s...", step->header.blockid); diff --git a/src/sacct/process.c b/src/sacct/process.c index 7c4eaf4ac33d3e3ec23a795c4abb70a983cf4d2c..75ca5cdcc5cbe6e7dcdf470d5ff01fad054c2b60 100644 --- a/src/sacct/process.c +++ b/src/sacct/process.c @@ -223,7 +223,7 @@ int _parse_line(char *f[], void **data) return SLURM_SUCCESS; } -void process_start(char *f[], int lc) +void process_start(char *f[], int lc, int show_full) { job_rec_t *job = NULL; job_rec_t *temp = NULL; @@ -241,13 +241,13 @@ void process_start(char *f[], int lc) } job = temp; - + job->show_full = show_full; list_append(jobs, job); job->job_start_seen = 1; } -void process_step(char *f[], int lc) +void process_step(char *f[], int lc, int show_full) { job_rec_t *job = NULL; @@ -264,13 +264,14 @@ void process_step(char *f[], int lc) } if (!job) { /* fake it for now */ job = _init_job_rec(temp->header); - if ((params.opt_verbose > 1) - && (params.opt_jobstep_list==NULL)) + if (params.opt_verbose > 1) fprintf(stderr, "Note: JOB_STEP record %u.%u preceded " "JOB_START record at line %d\n", temp->header.jobnum, temp->stepnum, lc); } + job->show_full = show_full; + if ((step = _find_step_record(job, temp->stepnum))) { if (temp->status == JOB_RUNNING) { @@ -358,36 +359,14 @@ got_step: step->rusage.ru_nswap); /* get the max for all the sacct_t struct */ - if(job->sacct.max_vsize < step->sacct.max_vsize) { - job->sacct.max_vsize = step->sacct.max_vsize; - job->sacct.max_vsize_task = step->sacct.max_vsize_task; - } - job->sacct.ave_vsize += step->sacct.ave_vsize; - - if(job->sacct.max_rss < step->sacct.max_rss) { - job->sacct.max_rss = step->sacct.max_rss; - job->sacct.max_rss_task = step->sacct.max_rss_task; - } - job->sacct.ave_rss += step->sacct.ave_rss; - - if(job->sacct.max_pages < step->sacct.max_pages) { - job->sacct.max_pages = step->sacct.max_pages; - job->sacct.max_pages_task = step->sacct.max_pages_task; - } - job->sacct.ave_pages += step->sacct.ave_pages; + aggregate_sacct(&job->sacct, &step->sacct); - if((job->sacct.min_cpu > step->sacct.min_cpu) - || (job->sacct.min_cpu == NO_VAL)) { - job->sacct.min_cpu = step->sacct.min_cpu; - job->sacct.min_cpu_task = step->sacct.min_cpu_task; - } - job->sacct.ave_cpu += step->sacct.ave_cpu; /* job->psize = MAX(job->psize, step->psize); */ /* job->vsize = MAX(job->vsize, step->vsize); */ job->ncpus = MAX(job->ncpus, step->ncpus); } -void process_suspend(char *f[], int lc) +void process_suspend(char *f[], int lc, int show_full) { job_rec_t *job = NULL; job_rec_t *temp = NULL; @@ -397,6 +376,7 @@ void process_suspend(char *f[], int lc) if (!job) job = _init_job_rec(temp->header); + job->show_full = show_full; if (job->status == JOB_SUSPENDED) job->elapsed -= temp->elapsed; @@ -405,7 +385,7 @@ void process_suspend(char *f[], int lc) destroy_job(temp); } -void process_terminated(char *f[], int lc) +void process_terminated(char *f[], int lc, int show_full) { job_rec_t *job = NULL; job_rec_t *temp = NULL; @@ -450,6 +430,8 @@ void process_terminated(char *f[], int lc) job->status = temp->status; if(list_count(job->steps) > 1) job->track_steps = 1; + job->show_full = show_full; + finished: destroy_job(temp); } @@ -466,6 +448,34 @@ void convert_num(float num, char *buf) snprintf(buf, 20, "%.2f%c", num, unit[count]); } +void aggregate_sacct(sacct_t *dest, sacct_t *from) +{ + if(dest->max_vsize < from->max_vsize) { + dest->max_vsize = from->max_vsize; + dest->max_vsize_task = from->max_vsize_task; + } + dest->ave_vsize += from->ave_vsize; + + if(dest->max_rss < from->max_rss) { + dest->max_rss = from->max_rss; + dest->max_rss_task = from->max_rss_task; + } + dest->ave_rss += from->ave_rss; + + if(dest->max_pages < from->max_pages) { + dest->max_pages = from->max_pages; + dest->max_pages_task = from->max_pages_task; + } + dest->ave_pages += from->ave_pages; + + if((dest->min_cpu > from->min_cpu) + || (dest->min_cpu == NO_VAL)) { + dest->min_cpu = from->min_cpu; + dest->min_cpu_task = from->min_cpu_task; + } + dest->ave_cpu += from->ave_cpu; +} + void destroy_acct_header(void *object) { acct_header_t *header = (acct_header_t *)object; diff --git a/src/sacct/sacct.c b/src/sacct/sacct.c index d7b5d9763609fac357373981888289548cacd807..40ce4ac8c8e0df91525d94f1dac722a1297c0bb0 100644 --- a/src/sacct/sacct.c +++ b/src/sacct/sacct.c @@ -192,6 +192,7 @@ int main(int argc, char **argv) EXPIRE, FDUMP, LIST, + STAT, HELP, USAGE } op; @@ -227,13 +228,14 @@ int main(int argc, char **argv) } } else if (params.opt_fdump) { op = FDUMP; + } else if (params.opt_stat) { + op = STAT; } else if (params.opt_expire) { op = EXPIRE; if (params.opt_long || params.opt_total || params.opt_field_list || (params.opt_gid>=0) || (params.opt_uid>=0) || - params.opt_job_list || params.opt_jobstep_list || - params.opt_state_list ) { + params.opt_job_list || params.opt_state_list ) { if (params.opt_verbose) fprintf(stderr, "Switch conflict,\n" @@ -243,7 +245,6 @@ int main(int argc, char **argv) "\topt_gid=%d\n" "\topt_uid=%d\n" "\topt_job_list=%s\n" - "\topt_jobstep_list=%s\n" "\topt_state_list=%s\n", params.opt_long, params.opt_total, @@ -251,12 +252,11 @@ int main(int argc, char **argv) params.opt_gid, params.opt_uid, params.opt_job_list, - params.opt_jobstep_list, params.opt_state_list); invalidSwitchCombo("--expire", "--brief, --long, --fields, " "--total, --gid, --uid, --jobs, " - "--jobstep, --state"); + "--state"); rc = 1; goto finished; } @@ -281,6 +281,11 @@ int main(int argc, char **argv) get_data(); do_list(); break; + case STAT: + if (params.opt_header) /* give them something to look */ + _print_header();/* at while we think... */ + do_stat(); + break; case HELP: do_help(); break; diff --git a/src/sacct/sacct.h b/src/sacct/sacct.h index c9616ddb0c1c9e8c9fdbb07749e4bfa24c973b8d..9ee59b0ef48b95157c437a8e2e8ff0864b56a4d9 100644 --- a/src/sacct/sacct.h +++ b/src/sacct/sacct.h @@ -42,11 +42,12 @@ #include <unistd.h> #include "src/common/getopt.h" -#include "src/common/slurm_protocol_api.h" #include "src/common/xmalloc.h" #include "src/common/xstring.h" #include "src/common/list.h" +#include "src/sacct/sacct_stat.h" + #define ERROR 2 /* slurmd uses "(uint32_t) -2" to track data for batch allocations @@ -173,27 +174,13 @@ typedef struct header { uint16_t rec_type; } acct_header_t; -typedef struct sacct_struct { - uint32_t max_vsize; - uint16_t max_vsize_task; - float ave_vsize; - uint32_t max_rss; - uint16_t max_rss_task; - float ave_rss; - uint32_t max_pages; - uint16_t max_pages_task; - float ave_pages; - float min_cpu; - uint16_t min_cpu_task; - float ave_cpu; -} sacct_t; - typedef struct job_rec { uint32_t job_start_seen, /* useful flags */ job_step_seen, job_terminated_seen, jobnum_superseded; /* older jobnum was reused */ acct_header_t header; + uint16_t show_full; char *nodes; char *jobname; uint16_t track_steps; @@ -241,6 +228,7 @@ typedef struct sacct_parameters { int opt_dump; /* --dump */ int opt_dup; /* --duplicates; +1 = explicitly set */ int opt_fdump; /* --formattted_dump */ + int opt_stat; /* --stat */ int opt_gid; /* --gid (-1=wildcard, 0=root) */ int opt_header; /* can only be cleared */ int opt_help; /* --help */ @@ -255,7 +243,6 @@ typedef struct sacct_parameters { char *opt_field_list; /* --fields= */ char *opt_filein; /* --file */ char *opt_job_list; /* --jobs */ - char *opt_jobstep_list; /* --jobstep */ char *opt_partition_list;/* --partitions */ char *opt_state_list; /* --states */ } sacct_parameters_t; @@ -271,11 +258,12 @@ extern int printfields[MAX_PRINTFIELDS], /* Indexed into fields[] */ nprintfields; /* process.c */ -void process_start(char *f[], int lc); -void process_step(char *f[], int lc); -void process_suspend(char *f[], int lc); -void process_terminated(char *f[], int lc); +void process_start(char *f[], int lc, int show_full); +void process_step(char *f[], int lc, int show_full); +void process_suspend(char *f[], int lc, int show_full); +void process_terminated(char *f[], int lc, int show_full); void convert_num(float num, char *buf); +void aggregate_sacct(sacct_t *dest, sacct_t *from); void destroy_acct_header(void *object); void destroy_job(void *object); void destroy_step(void *object); @@ -329,6 +317,7 @@ void do_expire(void); void do_fdump(char* fields[], int lc); void do_help(void); void do_list(void); +void do_stat(void); void sacct_init(); void sacct_fini(); diff --git a/src/slurmctld/agent.c b/src/slurmctld/agent.c index 9b3934be81213a87a7cdb340107eb1ee5cb8c0b4..5688dda75c2cc347f2169b62b918fbcdb2ac7ce0 100644 --- a/src/slurmctld/agent.c +++ b/src/slurmctld/agent.c @@ -707,7 +707,7 @@ static inline void _comm_err(char *node_name) */ static void *_thread_per_group_rpc(void *args) { - int rc = SLURM_SUCCESS, timeout = SLURM_MESSAGE_TIMEOUT_MSEC_STATIC; + int rc = SLURM_SUCCESS; slurm_msg_t msg; task_info_t *task_ptr = (task_info_t *) args; /* we cache some pointers from task_info_t because we need @@ -802,7 +802,8 @@ static void *_thread_per_group_rpc(void *args) //info("forwarding to %d",msg.forward.cnt); thread_ptr->end_time = thread_ptr->start_time + COMMAND_TIMEOUT; if (task_ptr->get_reply) { - if ((ret_list = slurm_send_recv_rc_msg(&msg, timeout)) + if ((ret_list = slurm_send_recv_rc_msg(&msg, + msg.forward.timeout)) == NULL) { if (!srun_agent) _comm_err(thread_ptr->node_name); diff --git a/src/slurmctld/proc_req.c b/src/slurmctld/proc_req.c index 24b012db1d3071e4f8db7b3a0fdbeed914734ac5..6aafb07a519a7dc9b5d4a32ce18b60d09fa22780 100644 --- a/src/slurmctld/proc_req.c +++ b/src/slurmctld/proc_req.c @@ -99,6 +99,7 @@ inline static void _slurm_rpc_shutdown_controller(slurm_msg_t * msg); inline static void _slurm_rpc_shutdown_controller_immediate(slurm_msg_t * msg); inline static void _slurm_rpc_step_complete(slurm_msg_t * msg); +inline static void _slurm_rpc_stat_jobacct(slurm_msg_t * msg); inline static void _slurm_rpc_submit_batch_job(slurm_msg_t * msg); inline static void _slurm_rpc_suspend(slurm_msg_t * msg); inline static void _slurm_rpc_update_job(slurm_msg_t * msg); @@ -276,6 +277,10 @@ void slurmctld_req (slurm_msg_t * msg) _slurm_rpc_step_complete(msg); slurm_free_step_complete_msg(msg->data); break; + case MESSAGE_STAT_JOBACCT: + _slurm_rpc_stat_jobacct(msg); + slurm_free_stat_jobacct_msg(msg->data); + break; default: error("invalid RPC msg_type=%d", msg->msg_type); slurm_send_rc_msg(msg, EINVAL); @@ -574,8 +579,8 @@ static void _slurm_rpc_dump_jobs(slurm_msg_t * msg) slurm_send_rc_msg(msg, SLURM_NO_CHANGE_IN_DATA); } else { pack_all_jobs(&dump, &dump_size, - job_info_request_msg->show_flags, - g_slurm_auth_get_uid(msg->cred)); + job_info_request_msg->show_flags, + g_slurm_auth_get_uid(msg->cred)); unlock_slurmctld(job_read_lock); END_TIMER; debug2("_slurm_rpc_dump_jobs, size=%d %s", @@ -1528,6 +1533,85 @@ static void _slurm_rpc_step_complete(slurm_msg_t *msg) (void) schedule_node_save(); /* Has own locking */ } +/* _slurm_rpc_step_complete - process step completion RPC to note the + * completion of a job step on at least some nodes. + * If the job step is complete, it may + * represent the termination of an entire job */ +static void _slurm_rpc_stat_jobacct(slurm_msg_t * msg) +{ + int error_code = SLURM_SUCCESS, i=0; + slurm_msg_t response_msg; + DEF_TIMERS; + stat_jobacct_msg_t *req = (stat_jobacct_msg_t *)msg->data; + resource_allocation_response_msg_t resp; + /* Locks: Write job, write node */ + slurmctld_lock_t job_read_lock = { + NO_LOCK, READ_LOCK, READ_LOCK, NO_LOCK }; + uid_t uid = g_slurm_auth_get_uid(msg->cred); + struct job_record *job_ptr = NULL; + struct step_record *step_ptr = NULL; + char bitstring[BUFFER_SIZE]; + int *node_pos = NULL; + int node_cnt = 0; + + + START_TIMER; + debug2("Processing RPC: MESSAGE_STAT_JOBACCT"); + + lock_slurmctld(job_read_lock); + error_code = old_job_info(uid, req->job_id, &job_ptr); + END_TIMER; + /* return result */ + if (error_code || (job_ptr == NULL)) { + unlock_slurmctld(job_read_lock); + debug2("_slurm_rpc_stat_jobacct: JobId=%u, uid=%u: %s", + req->job_id, uid, + slurm_strerror(error_code)); + slurm_send_rc_msg(msg, error_code); + return; + } else { + step_ptr = find_step_record(job_ptr, req->step_id); + if(!step_ptr) { + unlock_slurmctld(job_read_lock); + debug2("_slurm_rpc_stat_jobacct: " + "JobId=%u.%u Not Found", + req->job_id, req->step_id); + slurm_send_rc_msg(msg, ESLURM_INVALID_JOB_ID); + return; + } + node_cnt = bit_set_count(step_ptr->step_node_bitmap); + resp.node_addr = xmalloc(sizeof(slurm_addr) * node_cnt); + + bit_fmt(bitstring, BUFFER_SIZE, step_ptr->step_node_bitmap); + node_pos = bitfmt2int(bitstring); + + for(i=0; i < node_cnt; i++) { + if(node_pos[i] == -1) { + error("error with bitfmt2int"); + break; + } + memcpy(&resp.node_addr[i], + &job_ptr->node_addr[node_pos[i]], + sizeof(slurm_addr)); + } + resp.node_list = xstrdup(step_ptr->step_node_list); + resp.node_cnt = node_cnt; + resp.job_id = req->job_id; + resp.num_cpu_groups = 0; + resp.select_jobinfo = NULL; + unlock_slurmctld(job_read_lock); + response_msg.msg_type = RESPONSE_RESOURCE_ALLOCATION; + response_msg.data = &resp; + forward_init(&response_msg.forward, NULL); + response_msg.ret_list = NULL; + + slurm_send_node_msg(msg->conn_fd, &response_msg); + xfree(resp.node_list); + xfree(resp.node_addr); + xfree(node_pos); + } +} + /* _slurm_rpc_submit_batch_job - process RPC to submit a batch job */ static void _slurm_rpc_submit_batch_job(slurm_msg_t * msg) { diff --git a/src/slurmctld/step_mgr.c b/src/slurmctld/step_mgr.c index b4cdd52c599b363d122473e09c00fd79532174fd..150ec5e378a8489fea6d29933167e65f8428a9c5 100644 --- a/src/slurmctld/step_mgr.c +++ b/src/slurmctld/step_mgr.c @@ -461,7 +461,8 @@ try_again: if ((nodes_picked == NULL) || (nodes_idle == NULL)) fatal("bit_alloc malloc failure"); step_iterator = list_iterator_create (job_ptr->step_list); - while ((step_p = (struct step_record *) list_next (step_iterator))) + while ((step_p = (struct step_record *) + list_next(step_iterator))) bit_or (nodes_idle, step_p->step_node_bitmap); list_iterator_destroy (step_iterator); bit_not(nodes_idle); diff --git a/src/slurmd/common/stepd_api.c b/src/slurmd/common/stepd_api.c index b351c859c0384f1580518a810934a7512ac8908b..bc03f238a58497a2032f5d4f464a2232b17f379e 100644 --- a/src/slurmd/common/stepd_api.c +++ b/src/slurmd/common/stepd_api.c @@ -615,3 +615,33 @@ rwfail: return -1; } +/* + * + * Returns jobacctinfo_t struct on success, NULL if error. + * jobacctinfo_t must be freed after calling this function. + */ +int +stepd_stat_jobacct(int fd, stat_jobacct_msg_t *sent, stat_jobacct_msg_t *resp) +{ + int req = MESSAGE_STAT_JOBACCT; + int rc = SLURM_SUCCESS; + //jobacctinfo_t *jobacct = NULL; + int tasks = 0; + debug("Entering stepd_stat_jobacct for job %u.%u", + sent->job_id, sent->step_id); + safe_write(fd, &req, sizeof(int)); + + /* Receive the jobacct struct and return */ + resp->jobacct = jobacct_g_alloc((uint16_t)NO_VAL); + + rc = jobacct_g_getinfo(resp->jobacct, JOBACCT_DATA_PIPE, &fd); + safe_read(fd, &tasks, sizeof(int)); + resp->num_tasks = tasks; + return rc; +rwfail: + error("an error occured %d", rc); + jobacct_g_free(resp->jobacct); + resp->jobacct = NULL; + return rc; +} + diff --git a/src/slurmd/common/stepd_api.h b/src/slurmd/common/stepd_api.h index 7523e3564008e87741ceb3da3ca49aaaac82913f..af5480f4c9c06d92af9fa34232b2e1087405e05e 100644 --- a/src/slurmd/common/stepd_api.h +++ b/src/slurmd/common/stepd_api.h @@ -177,4 +177,12 @@ int stepd_resume(int fd); */ int stepd_completion(int fd, step_complete_msg_t *sent); +/* + * + * Returns SLURM_SUCCESS on success or SLURM_ERROR on error. + * resp recieves a jobacctinfo_t which must be freed if SUCCESS. + */ +int stepd_stat_jobacct(int fd, stat_jobacct_msg_t *sent, + stat_jobacct_msg_t *resp); + #endif /* _STEPD_API_H */ diff --git a/src/slurmd/slurmd/req.c b/src/slurmd/slurmd/req.c index 6059d1f3ab469fdf24c7a04dc3c9d3fbd00ad02e..0c234bc6960518f9e8c08d8db39637f75d99728d 100644 --- a/src/slurmd/slurmd/req.c +++ b/src/slurmd/slurmd/req.c @@ -50,6 +50,7 @@ #include "src/common/node_select.h" #include "src/common/slurm_auth.h" #include "src/common/slurm_cred.h" +#include "src/common/slurm_jobacct.h" #include "src/common/slurm_protocol_defs.h" #include "src/common/slurm_protocol_api.h" #include "src/common/slurm_protocol_interface.h" @@ -102,6 +103,7 @@ static void _rpc_pid2jid(slurm_msg_t *msg, slurm_addr *); static int _rpc_file_bcast(slurm_msg_t *msg, slurm_addr *); static int _rpc_ping(slurm_msg_t *, slurm_addr *); static int _rpc_step_complete(slurm_msg_t *msg, slurm_addr *cli_addr); +static int _rpc_stat_jobacct(slurm_msg_t *msg, slurm_addr *cli_addr); static int _run_prolog(uint32_t jobid, uid_t uid, char *bg_part_id); static int _run_epilog(uint32_t jobid, uid_t uid, char *bg_part_id); @@ -219,6 +221,10 @@ slurmd_req(slurm_msg_t *msg, slurm_addr *cli) rc = _rpc_step_complete(msg, cli); slurm_free_step_complete_msg(msg->data); break; + case MESSAGE_STAT_JOBACCT: + rc = _rpc_stat_jobacct(msg, cli); + slurm_free_stat_jobacct_msg(msg->data); + break; default: error("slurmd_req: invalid request msg type %d\n", msg->msg_type); @@ -1128,6 +1134,68 @@ done: slurm_send_rc_msg(msg, rc); } +static int +_rpc_stat_jobacct(slurm_msg_t *msg, slurm_addr *cli_addr) +{ + stat_jobacct_msg_t *req = (stat_jobacct_msg_t *)msg->data; + slurm_msg_t resp_msg; + stat_jobacct_msg_t *resp = NULL; + int fd; + uid_t req_uid; + uid_t job_uid; + + debug3("Entering _rpc_stat_jobacct"); + /* step completion messages are only allowed from other slurmstepd, + so only root or SlurmUser is allowed here */ + req_uid = g_slurm_auth_get_uid(msg->cred); + + job_uid = _get_job_uid(req->job_id); + /* + * check that requesting user ID is the SLURM UID or root + */ + if ((req_uid != job_uid) && (!_slurm_authorized_user(req_uid))) { + error("stat_jobacct from uid %ld for job %u " + "owned by uid %ld", + (long) req_uid, req->job_id, + (long) job_uid); + + if (msg->conn_fd >= 0) { + slurm_send_rc_msg(msg, ESLURM_USER_ID_MISSING); + return ESLURM_USER_ID_MISSING;/* or bad in this case */ + } + } + resp = xmalloc(sizeof(stat_jobacct_msg_t)); + resp->job_id = req->job_id; + resp->step_id = req->step_id; + + fd = stepd_connect(conf->spooldir, conf->node_name, + req->job_id, req->step_id); + if (fd == -1) { + error("stepd_connect to %u.%u failed: %m", + req->job_id, req->step_id); + slurm_send_rc_msg(msg, ESLURM_INVALID_JOB_ID); + slurm_free_stat_jobacct_msg(resp); + return ESLURM_INVALID_JOB_ID; + + } + if (stepd_stat_jobacct(fd, req, resp) == SLURM_ERROR) { + debug("kill for nonexistent job %u.%u requested", + req->job_id, req->step_id); + } + close(fd); + debug("job %u.%u found", resp->job_id, resp->step_id); + resp_msg.address = msg->address; + resp_msg.msg_type = MESSAGE_STAT_JOBACCT; + resp_msg.data = resp; + resp_msg.forward = msg->forward; + resp_msg.ret_list = msg->ret_list; + + slurm_send_node_msg(msg->conn_fd, &resp_msg); + slurm_free_stat_jobacct_msg(resp); + return SLURM_SUCCESS; +} + + /* * For the specified job_id: reply to slurmctld, * sleep(configured kill_wait), then send SIGKILL @@ -1685,7 +1753,7 @@ _rpc_signal_job(slurm_msg_t *msg, slurm_addr *cli) uid_t job_uid; List steps; ListIterator i; - step_loc_t *stepd; + step_loc_t *stepd = NULL; int step_cnt = 0; int fd; diff --git a/src/slurmd/slurmstepd/req.c b/src/slurmd/slurmstepd/req.c index 96f8035057559e72e716ca425c7ba99e697146fd..2d40aced47252f3563aacfff2278d923b045557b 100644 --- a/src/slurmd/slurmstepd/req.c +++ b/src/slurmd/slurmstepd/req.c @@ -67,6 +67,7 @@ static int _handle_suspend(int fd, slurmd_job_t *job, uid_t uid); static int _handle_resume(int fd, slurmd_job_t *job, uid_t uid); static int _handle_terminate(int fd, slurmd_job_t *job, uid_t uid); static int _handle_completion(int fd, slurmd_job_t *job, uid_t uid); +static int _handle_stat_jobacct(int fd, slurmd_job_t *job, uid_t uid); static bool _msg_socket_readable(eio_obj_t *obj); static int _msg_socket_accept(eio_obj_t *obj, List objs); @@ -465,6 +466,10 @@ _handle_request(int fd, slurmd_job_t *job, uid_t uid, gid_t gid) debug("Handling REQUEST_STEP_COMPLETION"); rc = _handle_completion(fd, job, uid); break; + case MESSAGE_STAT_JOBACCT: + debug("Handling MESSAGE_STAT_JOBACCT"); + rc = _handle_stat_jobacct(fd, job, uid); + break; default: error("Unrecognized request: %d", req); rc = SLURM_FAILURE; @@ -504,7 +509,7 @@ _handle_signal_process_group(int fd, slurmd_job_t *job, uid_t uid) int rc = SLURM_SUCCESS; int signal; - debug("_handle_signal_process_group for job %u.%u", + debug3("_handle_signal_process_group for job %u.%u", job->jobid, job->stepid); safe_read(fd, &signal, sizeof(int)); @@ -939,6 +944,7 @@ _handle_resume(int fd, slurmd_job_t *job, uid_t uid) goto done; } + jobacct_g_suspendpoll(); /* * Signal the container */ @@ -1025,3 +1031,42 @@ _handle_completion(int fd, slurmd_job_t *job, uid_t uid) rwfail: return SLURM_FAILURE; } + +static int +_handle_stat_jobacct(int fd, slurmd_job_t *job, uid_t uid) +{ + jobacctinfo_t *jobacct = NULL; + jobacctinfo_t *temp_jobacct = NULL; + int i = 0; + int num_tasks = 0; + debug("_handle_stat_jobacct for job %u.%u", + job->jobid, job->stepid); + + debug3(" uid = %d", uid); + if (uid != job->uid && !_slurm_authorized_user(uid)) { + debug("stat jobacct from uid %ld for job %u.%u " + "owned by uid %ld", + (long)uid, job->jobid, job->stepid, (long)job->uid); + /* Send NULL */ + jobacct_g_setinfo(jobacct, JOBACCT_DATA_PIPE, &fd); + return SLURM_ERROR; + } + + jobacct = jobacct_g_alloc((uint16_t)NO_VAL); + debug3("num tasks = %d", job->ntasks); + + for (i = 0; i < job->ntasks; i++) { + temp_jobacct = jobacct_g_stat_task(job->task[i]->pid); + if(temp_jobacct) { + jobacct_g_aggregate(jobacct, temp_jobacct); + num_tasks++; + } + } + debug3("got %d"); + jobacct_g_setinfo(jobacct, JOBACCT_DATA_PIPE, &fd); + safe_write(fd, &num_tasks, sizeof(int)); + jobacct_g_free(jobacct); + return SLURM_SUCCESS; +rwfail: + return SLURM_ERROR; +}