d5fd635efe
Refs https://github.com/open-mpi/ompi/pull/2425 Signed-off-by: Ralph Castain <rhc@open-mpi.org>
3258 строки
120 KiB
C
3258 строки
120 KiB
C
/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */
|
|
/*
|
|
* Copyright (c) 2004-2010 The Trustees of Indiana University and Indiana
|
|
* University Research and Technology
|
|
* Corporation. All rights reserved.
|
|
* Copyright (c) 2004-2008 The University of Tennessee and The University
|
|
* of Tennessee Research Foundation. All rights
|
|
* reserved.
|
|
* Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
|
|
* University of Stuttgart. All rights reserved.
|
|
* Copyright (c) 2004-2005 The Regents of the University of California.
|
|
* All rights reserved.
|
|
* Copyright (c) 2006-2016 Cisco Systems, Inc. All rights reserved.
|
|
* Copyright (c) 2007-2009 Sun Microsystems, Inc. All rights reserved.
|
|
* Copyright (c) 2007-2016 Los Alamos National Security, LLC. All rights
|
|
* reserved.
|
|
* Copyright (c) 2013-2016 Intel, Inc. All rights reserved.
|
|
* Copyright (c) 2015-2016 Research Organization for Information Science
|
|
* and Technology (RIST). All rights reserved.
|
|
* $COPYRIGHT$
|
|
*
|
|
* Additional copyrights may follow
|
|
*
|
|
* $HEADER$
|
|
*/
|
|
|
|
#include "orte_config.h"
|
|
#include "orte/constants.h"
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#ifdef HAVE_STRINGS_H
|
|
#include <strings.h>
|
|
#endif /* HAVE_STRINGS_H */
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_PARAM_H
|
|
#include <sys/param.h>
|
|
#endif
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <ctype.h>
|
|
#ifdef HAVE_SYS_TYPES_H
|
|
#include <sys/types.h>
|
|
#endif /* HAVE_SYS_TYPES_H */
|
|
#ifdef HAVE_SYS_WAIT_H
|
|
#include <sys/wait.h>
|
|
#endif /* HAVE_SYS_WAIT_H */
|
|
#ifdef HAVE_SYS_TIME_H
|
|
#include <sys/time.h>
|
|
#endif /* HAVE_SYS_TIME_H */
|
|
#include <fcntl.h>
|
|
#ifdef HAVE_SYS_STAT_H
|
|
#include <sys/stat.h>
|
|
#endif
|
|
|
|
#include "opal/dss/dss.h"
|
|
#include "opal/mca/event/event.h"
|
|
#include "opal/mca/installdirs/installdirs.h"
|
|
#include "opal/mca/hwloc/base/base.h"
|
|
#include "opal/mca/base/base.h"
|
|
#include "opal/mca/pmix/pmix.h"
|
|
#include "opal/util/argv.h"
|
|
#include "opal/util/output.h"
|
|
#include "opal/util/basename.h"
|
|
#include "opal/util/cmd_line.h"
|
|
#include "opal/util/opal_environ.h"
|
|
#include "opal/util/opal_getcwd.h"
|
|
#include "opal/util/show_help.h"
|
|
#include "opal/util/fd.h"
|
|
#include "opal/sys/atomic.h"
|
|
#if OPAL_ENABLE_FT_CR == 1
|
|
#include "opal/runtime/opal_cr.h"
|
|
#endif
|
|
|
|
#include "opal/version.h"
|
|
#include "opal/runtime/opal.h"
|
|
#include "opal/runtime/opal_info_support.h"
|
|
#include "opal/util/os_path.h"
|
|
#include "opal/util/path.h"
|
|
#include "opal/class/opal_pointer_array.h"
|
|
#include "opal/dss/dss.h"
|
|
|
|
#include "orte/mca/odls/odls_types.h"
|
|
#include "orte/mca/plm/plm.h"
|
|
#include "orte/mca/rmaps/rmaps_types.h"
|
|
#include "orte/mca/rmaps/base/base.h"
|
|
|
|
#include "orte/mca/errmgr/errmgr.h"
|
|
#include "orte/mca/grpcomm/grpcomm.h"
|
|
#include "orte/mca/plm/base/plm_private.h"
|
|
#include "orte/mca/rml/rml.h"
|
|
#include "orte/mca/rml/base/rml_contact.h"
|
|
#include "orte/mca/routed/routed.h"
|
|
#include "orte/mca/schizo/base/base.h"
|
|
#include "orte/mca/state/state.h"
|
|
|
|
#include "orte/runtime/runtime.h"
|
|
#include "orte/runtime/orte_globals.h"
|
|
#include "orte/runtime/orte_wait.h"
|
|
#include "orte/runtime/orte_quit.h"
|
|
#include "orte/util/pre_condition_transports.h"
|
|
#include "orte/util/show_help.h"
|
|
|
|
#include "orted_submit.h"
|
|
|
|
/**
|
|
* Global struct for catching orte command line options.
|
|
*/
|
|
orte_cmd_options_t orte_cmd_options = {0};
|
|
opal_cmd_line_t *orte_cmd_line = NULL;
|
|
|
|
static char **global_mca_env = NULL;
|
|
static orte_std_cntr_t total_num_apps = 0;
|
|
static bool want_prefix_by_default = (bool) ORTE_WANT_ORTERUN_PREFIX_BY_DEFAULT;
|
|
static opal_pointer_array_t tool_jobs;
|
|
static int timeout_seconds;
|
|
static orte_timer_t *orte_memprofile_timeout;
|
|
|
|
int orte_debugger_attach_fd = -1;
|
|
bool orte_debugger_fifo_active=false;
|
|
opal_event_t *orte_debugger_attach=NULL;
|
|
|
|
/*
|
|
* Local functions
|
|
*/
|
|
static int create_app(int argc, char* argv[],
|
|
orte_job_t *jdata,
|
|
orte_app_context_t **app,
|
|
bool *made_app, char ***app_env);
|
|
static int init_globals(void);
|
|
static int parse_globals(int argc, char* argv[], opal_cmd_line_t *cmd_line);
|
|
static int parse_locals(orte_job_t *jdata, int argc, char* argv[]);
|
|
static void set_classpath_jar_file(orte_app_context_t *app, int index, char *jarfile);
|
|
static int parse_appfile(orte_job_t *jdata, char *filename, char ***env);
|
|
static void orte_timeout_wakeup(int sd, short args, void *cbdata);
|
|
static void orte_profile_wakeup(int sd, short args, void *cbdata);
|
|
static void launch_recv(int status, orte_process_name_t* sender,
|
|
opal_buffer_t *buffer,
|
|
orte_rml_tag_t tag, void *cbdata);
|
|
static void complete_recv(int status, orte_process_name_t* sender,
|
|
opal_buffer_t *buffer,
|
|
orte_rml_tag_t tag, void *cbdata);
|
|
static void attach_debugger(int fd, short event, void *arg);
|
|
static void build_debugger_args(orte_app_context_t *debugger);
|
|
static void open_fifo (void);
|
|
static void run_debugger(char *basename, opal_cmd_line_t *cmd_line,
|
|
int argc, char *argv[], int num_procs);
|
|
|
|
/* instance the standard MPIR interfaces */
|
|
#define MPIR_MAX_PATH_LENGTH 512
|
|
#define MPIR_MAX_ARG_LENGTH 1024
|
|
struct MPIR_PROCDESC *MPIR_proctable = NULL;
|
|
int MPIR_proctable_size = 0;
|
|
volatile int MPIR_being_debugged = 0;
|
|
volatile int MPIR_debug_state = 0;
|
|
int MPIR_i_am_starter = 0;
|
|
int MPIR_partial_attach_ok = 1;
|
|
char MPIR_executable_path[MPIR_MAX_PATH_LENGTH] = {0};
|
|
char MPIR_server_arguments[MPIR_MAX_ARG_LENGTH] = {0};
|
|
volatile int MPIR_forward_output = 0;
|
|
volatile int MPIR_forward_comm = 0;
|
|
char MPIR_attach_fifo[MPIR_MAX_PATH_LENGTH] = {0};
|
|
int MPIR_force_to_main = 0;
|
|
static void orte_debugger_init_before_spawn(orte_job_t *jdata);
|
|
|
|
ORTE_DECLSPEC void* MPIR_Breakpoint(void);
|
|
|
|
/*
|
|
* Breakpoint function for parallel debuggers
|
|
*/
|
|
void* MPIR_Breakpoint(void)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* local objects */
|
|
typedef struct {
|
|
opal_object_t super;
|
|
orte_job_t *jdata;
|
|
int index;
|
|
orte_submit_cbfunc_t launch_cb;
|
|
void *launch_cbdata;
|
|
orte_submit_cbfunc_t complete_cb;
|
|
void *complete_cbdata;
|
|
} trackr_t;
|
|
static void tcon(trackr_t *p)
|
|
{
|
|
p->jdata = NULL;
|
|
p->launch_cb = NULL;
|
|
p->launch_cbdata = NULL;
|
|
p->complete_cb = NULL;
|
|
p->complete_cbdata = NULL;
|
|
}
|
|
static void tdes(trackr_t *p)
|
|
{
|
|
if (NULL != p->jdata) {
|
|
OBJ_RELEASE(p->jdata);
|
|
}
|
|
}
|
|
static OBJ_CLASS_INSTANCE(trackr_t,
|
|
opal_object_t,
|
|
tcon, tdes);
|
|
|
|
int orte_submit_init(int argc, char *argv[],
|
|
opal_cmd_line_init_t *opts)
|
|
{
|
|
int rc, i;
|
|
char *param;
|
|
|
|
/* init the globals */
|
|
memset(&orte_cmd_options, 0, sizeof(orte_cmd_options));
|
|
|
|
/* find our basename (the name of the executable) so that we can
|
|
use it in pretty-print error messages */
|
|
orte_basename = opal_basename(argv[0]);
|
|
|
|
/* search the argv for MCA params */
|
|
for (i=0; NULL != argv[i]; i++) {
|
|
if (':' == argv[i][0] ||
|
|
NULL == argv[i+1] || NULL == argv[i+2]) {
|
|
break;
|
|
}
|
|
if (0 == strncmp(argv[i], "-"OPAL_MCA_CMD_LINE_ID, strlen("-"OPAL_MCA_CMD_LINE_ID)) ||
|
|
0 == strncmp(argv[i], "--"OPAL_MCA_CMD_LINE_ID, strlen("--"OPAL_MCA_CMD_LINE_ID)) ||
|
|
0 == strncmp(argv[i], "-g"OPAL_MCA_CMD_LINE_ID, strlen("-g"OPAL_MCA_CMD_LINE_ID)) ||
|
|
0 == strncmp(argv[i], "--g"OPAL_MCA_CMD_LINE_ID, strlen("--g"OPAL_MCA_CMD_LINE_ID))) {
|
|
(void) mca_base_var_env_name (argv[i+1], ¶m);
|
|
opal_setenv(param, argv[i+2], true, &environ);
|
|
free(param);
|
|
} else if (0 == strcmp(argv[i], "-am") ||
|
|
0 == strcmp(argv[i], "--am")) {
|
|
(void)mca_base_var_env_name("mca_base_param_file_prefix", ¶m);
|
|
opal_setenv(param, argv[i+1], true, &environ);
|
|
free(param);
|
|
} else if (0 == strcmp(argv[i], "-tune") ||
|
|
0 == strcmp(argv[i], "--tune")) {
|
|
(void)mca_base_var_env_name("mca_base_envar_file_prefix", ¶m);
|
|
opal_setenv(param, argv[i+1], true, &environ);
|
|
free(param);
|
|
}
|
|
}
|
|
|
|
/* init only the util portion of OPAL */
|
|
if (OPAL_SUCCESS != (rc = opal_init_util(&argc, &argv))) {
|
|
return rc;
|
|
}
|
|
/* open the SCHIZO framework so we can setup the command line */
|
|
if (ORTE_SUCCESS != (rc = mca_base_framework_open(&orte_schizo_base_framework, 0))) {
|
|
ORTE_ERROR_LOG(rc);
|
|
return rc;
|
|
}
|
|
if (ORTE_SUCCESS != (rc = orte_schizo_base_select())) {
|
|
ORTE_ERROR_LOG(rc);
|
|
return rc;
|
|
}
|
|
|
|
OBJ_CONSTRUCT(&tool_jobs, opal_pointer_array_t);
|
|
opal_pointer_array_init(&tool_jobs, 256, INT_MAX, 128);
|
|
|
|
|
|
/* setup the cmd line */
|
|
orte_cmd_line = OBJ_NEW(opal_cmd_line_t);
|
|
|
|
/* if they were provided, add the opts */
|
|
if (NULL != opts) {
|
|
if (OPAL_SUCCESS != (rc = opal_cmd_line_add(orte_cmd_line, opts))) {
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
/* setup the rest of the cmd line only once */
|
|
if (OPAL_SUCCESS != (rc = orte_schizo.define_cli(orte_cmd_line))) {
|
|
return rc;
|
|
}
|
|
|
|
/* now that options have been defined, finish setup */
|
|
mca_base_cmd_line_setup(orte_cmd_line);
|
|
|
|
/* parse the result to get values */
|
|
if (OPAL_SUCCESS != (rc = opal_cmd_line_parse(orte_cmd_line,
|
|
true, false, argc, argv)) ) {
|
|
if (OPAL_ERR_SILENT != rc) {
|
|
fprintf(stderr, "%s: command line error (%s)\n", argv[0],
|
|
opal_strerror(rc));
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/* see if print version is requested. Do this before
|
|
* check for help so that --version --help works as
|
|
* one might expect. */
|
|
if (orte_cmd_options.version) {
|
|
char *str, *project_name = NULL;
|
|
if (0 == strcmp(orte_basename, "mpirun")) {
|
|
project_name = "Open MPI";
|
|
} else {
|
|
project_name = "OpenRTE";
|
|
}
|
|
str = opal_info_make_version_str("all",
|
|
OPAL_MAJOR_VERSION, OPAL_MINOR_VERSION,
|
|
OPAL_RELEASE_VERSION,
|
|
OPAL_GREEK_VERSION,
|
|
OPAL_REPO_REV);
|
|
if (NULL != str) {
|
|
fprintf(stdout, "%s (%s) %s\n\nReport bugs to %s\n",
|
|
orte_basename, project_name, str, PACKAGE_BUGREPORT);
|
|
free(str);
|
|
}
|
|
exit(0);
|
|
}
|
|
|
|
/* check if we are running as root - if we are, then only allow
|
|
* us to proceed if the allow-run-as-root flag was given. Otherwise,
|
|
* exit with a giant warning flag
|
|
*/
|
|
if (0 == geteuid() && !orte_cmd_options.run_as_root) {
|
|
fprintf(stderr, "--------------------------------------------------------------------------\n");
|
|
if (orte_cmd_options.help) {
|
|
fprintf(stderr, "%s cannot provide the help message when run as root.\n", orte_basename);
|
|
} else {
|
|
/* show_help is not yet available, so print an error manually */
|
|
fprintf(stderr, "%s has detected an attempt to run as root.\n", orte_basename);
|
|
}
|
|
fprintf(stderr, "Running as root is *strongly* discouraged as any mistake (e.g., in\n");
|
|
fprintf(stderr, "defining TMPDIR) or bug can result in catastrophic damage to the OS\n");
|
|
fprintf(stderr, "file system, leaving your system in an unusable state.\n\n");
|
|
fprintf(stderr, "You can override this protection by adding the --allow-run-as-root\n");
|
|
fprintf(stderr, "option to your cmd line. However, we reiterate our strong advice\n");
|
|
fprintf(stderr, "against doing so - please do so at your own risk.\n");
|
|
fprintf(stderr, "--------------------------------------------------------------------------\n");
|
|
exit(1);
|
|
}
|
|
|
|
/* process any mca params */
|
|
rc = mca_base_cmd_line_process_args(orte_cmd_line, &environ, &environ);
|
|
if (ORTE_SUCCESS != rc) {
|
|
return rc;
|
|
}
|
|
|
|
/* Need to initialize OPAL so that install_dirs are filled in */
|
|
if (OPAL_SUCCESS != (rc = opal_init(&argc, &argv))) {
|
|
return rc;
|
|
}
|
|
|
|
/* Check for help request */
|
|
if (orte_cmd_options.help) {
|
|
char *str, *args = NULL;
|
|
char *project_name = NULL;
|
|
if (0 == strcmp(orte_basename, "mpirun")) {
|
|
project_name = "Open MPI";
|
|
} else {
|
|
project_name = "OpenRTE";
|
|
}
|
|
args = opal_cmd_line_get_usage_msg(orte_cmd_line);
|
|
str = opal_show_help_string("help-orterun.txt", "orterun:usage", false,
|
|
orte_basename, project_name, OPAL_VERSION,
|
|
orte_basename, args,
|
|
PACKAGE_BUGREPORT);
|
|
if (NULL != str) {
|
|
printf("%s", str);
|
|
free(str);
|
|
}
|
|
free(args);
|
|
/* If someone asks for help, that should be all we do */
|
|
exit(0);
|
|
}
|
|
|
|
/* set the flags - if they gave us a -hnp option, then
|
|
* we are a tool. If not, then we are an HNP */
|
|
if (NULL == orte_cmd_options.hnp) {
|
|
orte_process_info.proc_type = ORTE_PROC_HNP;
|
|
} else {
|
|
orte_process_info.proc_type = ORTE_PROC_TOOL;
|
|
}
|
|
|
|
if (ORTE_PROC_IS_TOOL) {
|
|
if (0 == strncasecmp(orte_cmd_options.hnp, "file", strlen("file"))) {
|
|
char input[1024], *filename;
|
|
FILE *fp;
|
|
|
|
/* it is a file - get the filename */
|
|
filename = strchr(orte_cmd_options.hnp, ':');
|
|
if (NULL == filename) {
|
|
/* filename is not correctly formatted */
|
|
orte_show_help("help-orte-top.txt", "orte-top:hnp-filename-bad", true, "uri", orte_cmd_options.hnp);
|
|
exit(1);
|
|
}
|
|
++filename; /* space past the : */
|
|
|
|
if (0 >= strlen(filename)) {
|
|
/* they forgot to give us the name! */
|
|
orte_show_help("help-orte-top.txt", "orte-top:hnp-filename-bad", true, "uri", orte_cmd_options.hnp);
|
|
exit(1);
|
|
}
|
|
|
|
/* open the file and extract the uri */
|
|
fp = fopen(filename, "r");
|
|
if (NULL == fp) { /* can't find or read file! */
|
|
orte_show_help("help-orte-top.txt", "orte-top:hnp-filename-access", true, orte_cmd_options.hnp);
|
|
exit(1);
|
|
}
|
|
/* initialize the input to NULLs to ensure any input
|
|
* string is NULL-terminated */
|
|
memset(input, 0, 1024);
|
|
if (NULL == fgets(input, 1024, fp)) {
|
|
/* something malformed about file */
|
|
fclose(fp);
|
|
orte_show_help("help-orte-top.txt", "orte-top:hnp-file-bad", true, orte_cmd_options.hnp);
|
|
exit(1);
|
|
}
|
|
fclose(fp);
|
|
input[strlen(input)-1] = '\0'; /* remove newline */
|
|
/* construct the target hnp info */
|
|
opal_setenv(OPAL_MCA_PREFIX"orte_hnp_uri", input, true, &environ);
|
|
} else {
|
|
/* should just be the uri itself - construct the target hnp info */
|
|
opal_setenv(OPAL_MCA_PREFIX"orte_hnp_uri", orte_cmd_options.hnp, true, &environ);
|
|
}
|
|
/* we are never allowed to operate as a distributed tool,
|
|
* so insist on the ess/tool component */
|
|
opal_setenv(OPAL_MCA_PREFIX"ess", "tool", true, &environ);
|
|
} else {
|
|
/* may look strange, but the way we handle prefix is a little weird
|
|
* and probably needs to be addressed more fully at some future point.
|
|
* For now, we have a conflict between app_files and cmd line usage.
|
|
* Since app_files are used by the C/R system, we will make an
|
|
* adjustment here to avoid perturbing that system.
|
|
*
|
|
* We cannot just have the cmd line parser place any found value
|
|
* in the global struct as the app_file parser would replace it.
|
|
* So handle this specific cmd line option manually.
|
|
*/
|
|
orte_cmd_options.prefix = NULL;
|
|
orte_cmd_options.path_to_mpirun = NULL;
|
|
if (opal_cmd_line_is_taken(orte_cmd_line, "prefix") ||
|
|
'/' == argv[0][0] || want_prefix_by_default) {
|
|
size_t param_len;
|
|
if ('/' == argv[0][0]) {
|
|
char* tmp_basename = NULL;
|
|
/* If they specified an absolute path, strip off the
|
|
/bin/<exec_name>" and leave just the prefix */
|
|
orte_cmd_options.path_to_mpirun = opal_dirname(argv[0]);
|
|
/* Quick sanity check to ensure we got
|
|
something/bin/<exec_name> and that the installation
|
|
tree is at least more or less what we expect it to
|
|
be */
|
|
tmp_basename = opal_basename(orte_cmd_options.path_to_mpirun);
|
|
if (0 == strcmp("bin", tmp_basename)) {
|
|
char* tmp = orte_cmd_options.path_to_mpirun;
|
|
orte_cmd_options.path_to_mpirun = opal_dirname(tmp);
|
|
free(tmp);
|
|
} else {
|
|
free(orte_cmd_options.path_to_mpirun);
|
|
orte_cmd_options.path_to_mpirun = NULL;
|
|
}
|
|
free(tmp_basename);
|
|
}
|
|
/* if both are given, check to see if they match */
|
|
if (opal_cmd_line_is_taken(orte_cmd_line, "prefix") &&
|
|
NULL != orte_cmd_options.path_to_mpirun) {
|
|
char *tmp_basename;
|
|
/* if they don't match, then that merits a warning */
|
|
param = strdup(opal_cmd_line_get_param(orte_cmd_line, "prefix", 0, 0));
|
|
/* ensure we strip any trailing '/' */
|
|
if (0 == strcmp(OPAL_PATH_SEP, &(param[strlen(param)-1]))) {
|
|
param[strlen(param)-1] = '\0';
|
|
}
|
|
tmp_basename = strdup(orte_cmd_options.path_to_mpirun);
|
|
if (0 == strcmp(OPAL_PATH_SEP, &(tmp_basename[strlen(tmp_basename)-1]))) {
|
|
tmp_basename[strlen(tmp_basename)-1] = '\0';
|
|
}
|
|
if (0 != strcmp(param, tmp_basename)) {
|
|
orte_show_help("help-orterun.txt", "orterun:double-prefix",
|
|
true, orte_basename, orte_basename,
|
|
param, tmp_basename, orte_basename);
|
|
/* use the prefix over the path-to-mpirun so that
|
|
* people can specify the backend prefix as different
|
|
* from the local one
|
|
*/
|
|
free(orte_cmd_options.path_to_mpirun);
|
|
orte_cmd_options.path_to_mpirun = NULL;
|
|
}
|
|
free(tmp_basename);
|
|
} else if (NULL != orte_cmd_options.path_to_mpirun) {
|
|
param = strdup(orte_cmd_options.path_to_mpirun);
|
|
} else if (opal_cmd_line_is_taken(orte_cmd_line, "prefix")){
|
|
/* must be --prefix alone */
|
|
param = strdup(opal_cmd_line_get_param(orte_cmd_line, "prefix", 0, 0));
|
|
} else {
|
|
/* --enable-orterun-prefix-default was given to orterun */
|
|
param = strdup(opal_install_dirs.prefix);
|
|
}
|
|
|
|
if (NULL != param) {
|
|
/* "Parse" the param, aka remove superfluous path_sep. */
|
|
param_len = strlen(param);
|
|
while (0 == strcmp (OPAL_PATH_SEP, &(param[param_len-1]))) {
|
|
param[param_len-1] = '\0';
|
|
param_len--;
|
|
if (0 == param_len) {
|
|
orte_show_help("help-orterun.txt", "orterun:empty-prefix",
|
|
true, orte_basename, orte_basename);
|
|
free(param);
|
|
return ORTE_ERR_FATAL;
|
|
}
|
|
}
|
|
|
|
orte_cmd_options.prefix = param;
|
|
}
|
|
want_prefix_by_default = true;
|
|
}
|
|
}
|
|
|
|
/* Setup MCA params */
|
|
orte_register_params();
|
|
|
|
if (orte_cmd_options.debug) {
|
|
orte_devel_level_output = true;
|
|
}
|
|
|
|
/* Initialize our Open RTE environment
|
|
* Set the flag telling orte_init that I am NOT a
|
|
* singleton, but am "infrastructure" - prevents setting
|
|
* up incorrect infrastructure that only a singleton would
|
|
* require
|
|
*/
|
|
if (ORTE_SUCCESS != (rc = orte_init(&argc, &argv,
|
|
orte_process_info.proc_type))) {
|
|
/* cannot call ORTE_ERROR_LOG as it could be the errmgr
|
|
* never got loaded!
|
|
*/
|
|
return rc;
|
|
}
|
|
/* finalize OPAL. As it was opened again from orte_init->opal_init
|
|
* we continue to have a reference count on it. So we have to finalize it twice...
|
|
*/
|
|
opal_finalize();
|
|
|
|
/* clear params from the environment so our children
|
|
* don't pick them up */
|
|
opal_unsetenv(OPAL_MCA_PREFIX"ess", &environ);
|
|
opal_unsetenv(OPAL_MCA_PREFIX"pmix", &environ);
|
|
|
|
if (ORTE_PROC_IS_TOOL) {
|
|
/* set the info in our contact table */
|
|
orte_rml.set_contact_info(orte_process_info.my_hnp_uri);
|
|
/* extract the name */
|
|
if (ORTE_SUCCESS != orte_rml_base_parse_uris(orte_process_info.my_hnp_uri, ORTE_PROC_MY_HNP, NULL)) {
|
|
orte_show_help("help-orte-top.txt", "orte-top:hnp-uri-bad", true, orte_process_info.my_hnp_uri);
|
|
exit(1);
|
|
}
|
|
/* set the route to be direct */
|
|
if (ORTE_SUCCESS != orte_routed.update_route(NULL, ORTE_PROC_MY_HNP, ORTE_PROC_MY_HNP)) {
|
|
orte_show_help("help-orte-top.txt", "orte-top:hnp-uri-bad", true, orte_process_info.my_hnp_uri);
|
|
orte_finalize();
|
|
exit(1);
|
|
}
|
|
|
|
/* set the target hnp as our lifeline so we will terminate if it exits */
|
|
orte_routed.set_lifeline(NULL, ORTE_PROC_MY_HNP);
|
|
|
|
/* setup to listen for HNP response to my commands */
|
|
orte_rml.recv_buffer_nb(ORTE_NAME_WILDCARD, ORTE_RML_TAG_NOTIFY_COMPLETE,
|
|
ORTE_RML_PERSISTENT, complete_recv, NULL);
|
|
orte_rml.recv_buffer_nb(ORTE_NAME_WILDCARD, ORTE_RML_TAG_LAUNCH_RESP,
|
|
ORTE_RML_PERSISTENT, launch_recv, NULL);
|
|
} else {
|
|
/* save the environment for launch purposes. This MUST be
|
|
* done so that we can pass it to any local procs we
|
|
* spawn - otherwise, those local procs won't see any
|
|
* non-MCA envars were set in the enviro prior to calling
|
|
* orterun
|
|
*/
|
|
orte_launch_environ = opal_argv_copy(environ);
|
|
}
|
|
|
|
return ORTE_SUCCESS;
|
|
}
|
|
|
|
|
|
void orte_submit_finalize(void)
|
|
{
|
|
trackr_t *trk;
|
|
int i;
|
|
|
|
orte_rml.recv_cancel(ORTE_NAME_WILDCARD, ORTE_RML_TAG_LAUNCH_RESP);
|
|
orte_rml.recv_cancel(ORTE_NAME_WILDCARD, ORTE_RML_TAG_NOTIFY_COMPLETE);
|
|
for (i=0; i < tool_jobs.size; i++) {
|
|
if (NULL != (trk = (trackr_t*)opal_pointer_array_get_item(&tool_jobs, i))) {
|
|
OBJ_RELEASE(trk);
|
|
}
|
|
}
|
|
OBJ_DESTRUCT(&tool_jobs);
|
|
|
|
/* destruct the cmd line object */
|
|
if (NULL != orte_cmd_line) {
|
|
OBJ_RELEASE(orte_cmd_line);
|
|
}
|
|
|
|
/* if it was created, remove the debugger attach fifo */
|
|
if (0 <= orte_debugger_attach_fd) {
|
|
if (orte_debugger_fifo_active) {
|
|
opal_event_del(orte_debugger_attach);
|
|
free(orte_debugger_attach);
|
|
}
|
|
close(orte_debugger_attach_fd);
|
|
unlink(MPIR_attach_fifo);
|
|
}
|
|
}
|
|
|
|
int orte_submit_cancel(int index) {
|
|
|
|
int rc;
|
|
trackr_t *trk;
|
|
opal_buffer_t *req;
|
|
orte_daemon_cmd_flag_t cmd = ORTE_DAEMON_TERMINATE_JOB_CMD;
|
|
|
|
/* get the tracker */
|
|
if (NULL == (trk = (trackr_t*)opal_pointer_array_get_item(&tool_jobs, index))) {
|
|
opal_output(0, "TRACKER ID %d RETURNED INDEX TO NULL OBJECT", index);
|
|
return ORTE_ERROR;
|
|
}
|
|
|
|
/* create and send request with command and jobid */
|
|
req = OBJ_NEW(opal_buffer_t);
|
|
if (OPAL_SUCCESS != (rc = opal_dss.pack(req, &cmd, 1, ORTE_DAEMON_CMD))) {
|
|
ORTE_ERROR_LOG(rc);
|
|
return rc;
|
|
}
|
|
if (OPAL_SUCCESS != (rc = opal_dss.pack(req, &trk->jdata->jobid, 1, ORTE_JOBID))) {
|
|
ORTE_ERROR_LOG(rc);
|
|
return rc;
|
|
}
|
|
rc = orte_rml.send_buffer_nb(orte_mgmt_conduit,
|
|
ORTE_PROC_MY_HNP, req, ORTE_RML_TAG_DAEMON,
|
|
orte_rml_send_callback, NULL);
|
|
if (ORTE_SUCCESS != rc) {
|
|
ORTE_ERROR_LOG(rc);
|
|
OBJ_RELEASE(req);
|
|
return rc;
|
|
}
|
|
|
|
return ORTE_ERR_OP_IN_PROGRESS;
|
|
}
|
|
|
|
|
|
int orte_submit_halt(void)
|
|
{
|
|
int rc;
|
|
opal_buffer_t *req;
|
|
orte_daemon_cmd_flag_t cmd = ORTE_DAEMON_HALT_DVM_CMD;
|
|
|
|
req = OBJ_NEW(opal_buffer_t);
|
|
if (OPAL_SUCCESS != (rc = opal_dss.pack(req, &cmd, 1, ORTE_DAEMON_CMD))) {
|
|
ORTE_ERROR_LOG(rc);
|
|
return rc;
|
|
}
|
|
rc = orte_rml.send_buffer_nb(orte_mgmt_conduit,
|
|
ORTE_PROC_MY_HNP, req,
|
|
ORTE_RML_TAG_DAEMON,
|
|
orte_rml_send_callback, NULL);
|
|
if (ORTE_SUCCESS != rc) {
|
|
ORTE_ERROR_LOG(rc);
|
|
OBJ_RELEASE(req);
|
|
return rc;
|
|
}
|
|
|
|
return ORTE_ERR_OP_IN_PROGRESS;
|
|
}
|
|
|
|
//
|
|
// The real thing
|
|
//
|
|
int orte_submit_job(char *argv[], int *index,
|
|
orte_submit_cbfunc_t launch_cb,
|
|
void *launch_cbdata,
|
|
orte_submit_cbfunc_t complete_cb,
|
|
void *complete_cbdata)
|
|
{
|
|
opal_buffer_t *req;
|
|
int rc, n;
|
|
orte_app_idx_t i;
|
|
orte_daemon_cmd_flag_t cmd = ORTE_DAEMON_SPAWN_JOB_CMD;
|
|
char *param;
|
|
orte_job_t *jdata = NULL, *daemons;
|
|
orte_app_context_t *app, *dapp;
|
|
trackr_t *trk;
|
|
int argc;
|
|
|
|
/* bozo check - we don't allow recursive calls of submit */
|
|
if (NULL != getenv("OMPI_UNIVERSE_SIZE")) {
|
|
fprintf(stderr, "\n\n**********************************************************\n\n");
|
|
fprintf(stderr, "%s does not support recursive calls\n", orte_basename);
|
|
fprintf(stderr, "\n**********************************************************\n");
|
|
return ORTE_ERR_FATAL;
|
|
}
|
|
|
|
/* reset the globals every time thru as the argv
|
|
* will modify them */
|
|
memset(&orte_cmd_options, 0, sizeof(orte_cmd_options));
|
|
argc = opal_argv_count(argv);
|
|
|
|
/* parse the cmd line - do this every time thru so we can
|
|
* repopulate the globals */
|
|
if (OPAL_SUCCESS != (rc = opal_cmd_line_parse(orte_cmd_line, true, false,
|
|
argc, argv)) ) {
|
|
if (OPAL_ERR_SILENT != rc) {
|
|
fprintf(stderr, "%s: command line error (%s)\n", argv[0],
|
|
opal_strerror(rc));
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/* Check for some "global" command line params */
|
|
parse_globals(argc, argv, orte_cmd_line);
|
|
|
|
/* create a new job object to hold the info for this one - the
|
|
* jobid field will be filled in by the PLM when the job is
|
|
* launched
|
|
*/
|
|
jdata = OBJ_NEW(orte_job_t);
|
|
if (NULL == jdata) {
|
|
/* cannot call ORTE_ERROR_LOG as the errmgr
|
|
* hasn't been loaded yet!
|
|
*/
|
|
return ORTE_ERR_OUT_OF_RESOURCE;
|
|
}
|
|
/* see if they specified the personality */
|
|
if (NULL != orte_cmd_options.personality) {
|
|
jdata->personality = opal_argv_split(orte_cmd_options.personality, ',');
|
|
} else {
|
|
/* default to OMPI */
|
|
opal_argv_append_nosize(&jdata->personality, "ompi");
|
|
}
|
|
|
|
trk = OBJ_NEW(trackr_t);
|
|
trk->jdata = jdata;
|
|
trk->launch_cb = launch_cb;
|
|
trk->launch_cbdata = launch_cbdata;
|
|
trk->complete_cb = complete_cb;
|
|
trk->complete_cbdata = complete_cbdata;
|
|
trk->index = opal_pointer_array_add(&tool_jobs, trk);
|
|
|
|
|
|
/* pass our tracker ID */
|
|
orte_set_attribute(&jdata->attributes, ORTE_JOB_ROOM_NUM, ORTE_ATTR_GLOBAL, &trk->index, OPAL_INT);
|
|
|
|
/* check for stdout/err directives */
|
|
/* if we were asked to tag output, mark it so */
|
|
if (orte_cmd_options.tag_output) {
|
|
orte_set_attribute(&jdata->attributes, ORTE_JOB_TAG_OUTPUT, ORTE_ATTR_GLOBAL, NULL, OPAL_BOOL);
|
|
}
|
|
/* if we were asked to timestamp output, mark it so */
|
|
if (orte_cmd_options.timestamp_output) {
|
|
orte_set_attribute(&jdata->attributes, ORTE_JOB_TIMESTAMP_OUTPUT, ORTE_ATTR_GLOBAL, NULL, OPAL_BOOL);
|
|
}
|
|
/* if we were asked to output to files, pass it along */
|
|
if (NULL != orte_cmd_options.output_filename) {
|
|
orte_set_attribute(&jdata->attributes, ORTE_JOB_OUTPUT_TO_FILE, ORTE_ATTR_GLOBAL, orte_cmd_options.output_filename, OPAL_STRING);
|
|
}
|
|
/* if we were asked to merge stderr to stdout, mark it so */
|
|
if (orte_cmd_options.merge) {
|
|
orte_set_attribute(&jdata->attributes, ORTE_JOB_MERGE_STDERR_STDOUT, ORTE_ATTR_GLOBAL, NULL, OPAL_BOOL);
|
|
}
|
|
|
|
|
|
/* check what user wants us to do with stdin */
|
|
if (NULL != orte_cmd_options.stdin_target) {
|
|
if (0 == strcmp(orte_cmd_options.stdin_target, "all")) {
|
|
jdata->stdin_target = ORTE_VPID_WILDCARD;
|
|
} else if (0 == strcmp(orte_cmd_options.stdin_target, "none")) {
|
|
jdata->stdin_target = ORTE_VPID_INVALID;
|
|
} else {
|
|
jdata->stdin_target = strtoul(orte_cmd_options.stdin_target, NULL, 10);
|
|
}
|
|
}
|
|
|
|
/* if we want the argv's indexed, indicate that */
|
|
if (orte_cmd_options.index_argv) {
|
|
orte_set_attribute(&jdata->attributes, ORTE_JOB_INDEX_ARGV, ORTE_ATTR_GLOBAL, NULL, OPAL_BOOL);
|
|
}
|
|
|
|
/* Parse each app, adding it to the job object */
|
|
parse_locals(jdata, argc, argv);
|
|
|
|
if (0 == jdata->num_apps) {
|
|
/* This should never happen -- this case should be caught in
|
|
create_app(), but let's just double check... */
|
|
orte_show_help("help-orterun.txt", "orterun:nothing-to-do",
|
|
true, orte_basename);
|
|
ORTE_UPDATE_EXIT_STATUS(ORTE_ERROR_DEFAULT_EXIT_CODE);
|
|
return ORTE_ERR_FATAL;
|
|
}
|
|
|
|
/* create the map object to communicate policies */
|
|
jdata->map = OBJ_NEW(orte_job_map_t);
|
|
|
|
if (NULL != orte_cmd_options.mapping_policy) {
|
|
if (ORTE_SUCCESS != (rc = orte_rmaps_base_set_mapping_policy(&jdata->map->mapping, NULL, orte_cmd_options.mapping_policy))) {
|
|
ORTE_ERROR_LOG(rc);
|
|
return rc;
|
|
}
|
|
} else if (orte_cmd_options.pernode) {
|
|
ORTE_SET_MAPPING_POLICY(jdata->map->mapping, ORTE_MAPPING_PPR);
|
|
ORTE_SET_MAPPING_DIRECTIVE(jdata->map->mapping, ORTE_MAPPING_GIVEN);
|
|
/* define the ppr */
|
|
jdata->map->ppr = strdup("1:node");
|
|
} else if (0 < orte_cmd_options.npernode) {
|
|
ORTE_SET_MAPPING_POLICY(jdata->map->mapping, ORTE_MAPPING_PPR);
|
|
ORTE_SET_MAPPING_DIRECTIVE(jdata->map->mapping, ORTE_MAPPING_GIVEN);
|
|
/* define the ppr */
|
|
(void)asprintf(&jdata->map->ppr, "%d:node", orte_cmd_options.npernode);
|
|
} else if (0 < orte_cmd_options.npersocket) {
|
|
ORTE_SET_MAPPING_POLICY(jdata->map->mapping, ORTE_MAPPING_PPR);
|
|
ORTE_SET_MAPPING_DIRECTIVE(jdata->map->mapping, ORTE_MAPPING_GIVEN);
|
|
/* define the ppr */
|
|
(void)asprintf(&jdata->map->ppr, "%d:socket", orte_cmd_options.npersocket);
|
|
}
|
|
|
|
|
|
/* if the user specified cpus/rank, set it */
|
|
if (0 < orte_cmd_options.cpus_per_proc) {
|
|
jdata->map->cpus_per_rank = orte_cmd_options.cpus_per_proc;
|
|
}
|
|
/* if the user specified a ranking policy, then set it */
|
|
if (NULL != orte_cmd_options.ranking_policy) {
|
|
if (ORTE_SUCCESS != (rc = orte_rmaps_base_set_ranking_policy(&jdata->map->ranking,
|
|
jdata->map->mapping,
|
|
orte_cmd_options.ranking_policy))) {
|
|
ORTE_ERROR_LOG(rc);
|
|
return rc;
|
|
}
|
|
}
|
|
/* if the user specified a binding policy, then set it */
|
|
if (NULL != orte_cmd_options.binding_policy) {
|
|
if (ORTE_SUCCESS != (rc = opal_hwloc_base_set_binding_policy(&jdata->map->binding,
|
|
orte_cmd_options.binding_policy))) {
|
|
ORTE_ERROR_LOG(rc);
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
/* if they asked for nolocal, mark it so */
|
|
if (orte_cmd_options.nolocal) {
|
|
ORTE_SET_MAPPING_DIRECTIVE(jdata->map->mapping, ORTE_MAPPING_NO_USE_LOCAL);
|
|
}
|
|
if (orte_cmd_options.no_oversubscribe) {
|
|
ORTE_UNSET_MAPPING_DIRECTIVE(jdata->map->mapping, ORTE_MAPPING_NO_OVERSUBSCRIBE);
|
|
}
|
|
if (orte_cmd_options.oversubscribe) {
|
|
ORTE_UNSET_MAPPING_DIRECTIVE(jdata->map->mapping, ORTE_MAPPING_NO_OVERSUBSCRIBE);
|
|
}
|
|
if (orte_cmd_options.report_bindings) {
|
|
orte_set_attribute(&jdata->attributes, ORTE_JOB_REPORT_BINDINGS, ORTE_ATTR_GLOBAL, NULL, OPAL_BOOL);
|
|
}
|
|
if (orte_cmd_options.slot_list) {
|
|
orte_set_attribute(&jdata->attributes, ORTE_JOB_SLOT_LIST, ORTE_ATTR_GLOBAL, orte_cmd_options.slot_list, OPAL_STRING);
|
|
}
|
|
|
|
/* if recovery was enabled on the cmd line, do so */
|
|
if (orte_enable_recovery) {
|
|
ORTE_FLAG_SET(jdata, ORTE_JOB_FLAG_RECOVERABLE);
|
|
if (0 == orte_max_restarts) {
|
|
/* mark this job as continuously operating */
|
|
orte_set_attribute(&jdata->attributes, ORTE_JOB_CONTINUOUS_OP, ORTE_ATTR_GLOBAL, NULL, OPAL_BOOL);
|
|
}
|
|
}
|
|
/* record the max restarts */
|
|
if (0 < orte_max_restarts) {
|
|
for (i=0; i < jdata->num_apps; i++) {
|
|
if (NULL != (app = (orte_app_context_t*)opal_pointer_array_get_item(jdata->apps, i))) {
|
|
orte_set_attribute(&app->attributes, ORTE_APP_MAX_RESTARTS, ORTE_ATTR_GLOBAL, &orte_max_restarts, OPAL_INT32);
|
|
}
|
|
}
|
|
}
|
|
/* if continuous operation was specified */
|
|
if (orte_cmd_options.continuous) {
|
|
/* mark this job as continuously operating */
|
|
orte_set_attribute(&jdata->attributes, ORTE_JOB_CONTINUOUS_OP, ORTE_ATTR_GLOBAL, NULL, OPAL_BOOL);
|
|
}
|
|
|
|
/* check for debugger test envars and forward them if necessary */
|
|
if (NULL != getenv("ORTE_TEST_DEBUGGER_ATTACH")) {
|
|
char *evar;
|
|
evar = getenv("ORTE_TEST_DEBUGGER_SLEEP");
|
|
for (n=0; n < (int)jdata->num_apps; n++) {
|
|
if (NULL != (app = (orte_app_context_t*)opal_pointer_array_get_item(jdata->apps, n))) {
|
|
opal_setenv("ORTE_TEST_DEBUGGER_ATTACH", "1", true, &app->env);
|
|
if (NULL != evar) {
|
|
opal_setenv("ORTE_TEST_DEBUGGER_SLEEP", evar, true, &app->env);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* check for debugger test envars and forward them if necessary */
|
|
if (NULL != getenv("ORTE_TEST_DEBUGGER_ATTACH")) {
|
|
char *evar;
|
|
evar = getenv("ORTE_TEST_DEBUGGER_SLEEP");
|
|
for (i=0; i < (int)jdata->num_apps; i++) {
|
|
if (NULL != (app = (orte_app_context_t*)opal_pointer_array_get_item(jdata->apps, i))) {
|
|
opal_setenv("ORTE_TEST_DEBUGGER_ATTACH", "1", true, &app->env);
|
|
if (NULL != evar) {
|
|
opal_setenv("ORTE_TEST_DEBUGGER_SLEEP", evar, true, &app->env);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* check for suicide test directives */
|
|
if (NULL != getenv("ORTE_TEST_HNP_SUICIDE") ||
|
|
NULL != getenv("ORTE_TEST_ORTED_SUICIDE")) {
|
|
/* don't forward IO from this process so we can
|
|
* see any debug after daemon termination */
|
|
ORTE_FLAG_UNSET(jdata, ORTE_JOB_FLAG_FORWARD_OUTPUT);
|
|
}
|
|
|
|
/* check for a job timeout specification, to be provided in seconds
|
|
* as that is what MPICH used
|
|
*/
|
|
param = NULL;
|
|
if (0 < orte_cmd_options.timeout ||
|
|
NULL != (param = getenv("MPIEXEC_TIMEOUT"))) {
|
|
if (NULL != param) {
|
|
timeout_seconds = strtol(param, NULL, 10);
|
|
/* both cannot be present, or they must agree */
|
|
if (0 < orte_cmd_options.timeout && timeout_seconds != orte_cmd_options.timeout) {
|
|
orte_show_help("help-orterun.txt", "orterun:timeoutconflict", false,
|
|
orte_basename, orte_cmd_options.timeout, param);
|
|
exit(1);
|
|
}
|
|
} else {
|
|
timeout_seconds = orte_cmd_options.timeout;
|
|
}
|
|
if (NULL == (orte_mpiexec_timeout = OBJ_NEW(orte_timer_t))) {
|
|
ORTE_ERROR_LOG(ORTE_ERR_OUT_OF_RESOURCE);
|
|
ORTE_UPDATE_EXIT_STATUS(ORTE_ERR_OUT_OF_RESOURCE);
|
|
//goto DONE;
|
|
}
|
|
orte_mpiexec_timeout->tv.tv_sec = timeout_seconds;
|
|
orte_mpiexec_timeout->tv.tv_usec = 0;
|
|
opal_event_evtimer_set(orte_event_base, orte_mpiexec_timeout->ev,
|
|
orte_timeout_wakeup, jdata);
|
|
opal_event_set_priority(orte_mpiexec_timeout->ev, ORTE_ERROR_PRI);
|
|
opal_event_evtimer_add(orte_mpiexec_timeout->ev, &orte_mpiexec_timeout->tv);
|
|
}
|
|
|
|
/* check for diagnostic memory profile */
|
|
if (NULL != (param = getenv("OMPI_MEMPROFILE"))) {
|
|
timeout_seconds = strtol(param, NULL, 10);
|
|
if (NULL == (orte_memprofile_timeout = OBJ_NEW(orte_timer_t))) {
|
|
ORTE_ERROR_LOG(ORTE_ERR_OUT_OF_RESOURCE);
|
|
ORTE_UPDATE_EXIT_STATUS(ORTE_ERR_OUT_OF_RESOURCE);
|
|
//goto DONE;
|
|
}
|
|
orte_memprofile_timeout->tv.tv_sec = timeout_seconds;
|
|
orte_memprofile_timeout->tv.tv_usec = 0;
|
|
opal_event_evtimer_set(orte_event_base, orte_memprofile_timeout->ev,
|
|
orte_profile_wakeup, jdata);
|
|
opal_event_set_priority(orte_memprofile_timeout->ev, ORTE_ERROR_PRI);
|
|
opal_event_evtimer_add(orte_memprofile_timeout->ev, &orte_memprofile_timeout->tv);
|
|
}
|
|
if (ORTE_PROC_IS_HNP) {
|
|
/* get the daemon job object */
|
|
daemons = orte_get_job_data_object(ORTE_PROC_MY_NAME->jobid);
|
|
|
|
/* check for request to report uri */
|
|
if (NULL != orte_cmd_options.report_uri) {
|
|
FILE *fp;
|
|
char *rml_uri;
|
|
rml_uri = orte_rml.get_contact_info();
|
|
if (0 == strcmp(orte_cmd_options.report_uri, "-")) {
|
|
/* if '-', then output to stdout */
|
|
printf("%s\n", (NULL == rml_uri) ? "NULL" : rml_uri);
|
|
} else if (0 == strcmp(orte_cmd_options.report_uri, "+")) {
|
|
/* if '+', output to stderr */
|
|
fprintf(stderr, "%s\n", (NULL == rml_uri) ? "NULL" : rml_uri);
|
|
} else {
|
|
fp = fopen(orte_cmd_options.report_uri, "w");
|
|
if (NULL == fp) {
|
|
orte_show_help("help-orterun.txt", "orterun:write_file", false,
|
|
orte_basename, "uri", orte_cmd_options.report_uri);
|
|
exit(1);
|
|
}
|
|
fprintf(fp, "%s\n", (NULL == rml_uri) ? "NULL" : rml_uri);
|
|
fclose(fp);
|
|
}
|
|
if (NULL != rml_uri) {
|
|
free(rml_uri);
|
|
}
|
|
}
|
|
/* If we have a prefix, then modify the PATH and
|
|
LD_LIBRARY_PATH environment variables in our copy. This
|
|
will ensure that any locally-spawned children will
|
|
have our executables and libraries in their path
|
|
|
|
For now, default to the prefix_dir provided in the first app_context.
|
|
Since there always MUST be at least one app_context, we are safe in
|
|
doing this.
|
|
*/
|
|
param = NULL;
|
|
if (NULL != (app = (orte_app_context_t*)opal_pointer_array_get_item(jdata->apps, 0)) &&
|
|
orte_get_attribute(&app->attributes, ORTE_APP_PREFIX_DIR, (void**)¶m, OPAL_STRING)) {
|
|
char *oldenv, *newenv, *lib_base, *bin_base;
|
|
|
|
/* copy the prefix into the daemon job so that any launcher
|
|
* can find the orteds when we launch the virtual machine
|
|
*/
|
|
if (NULL == (dapp = (orte_app_context_t*)opal_pointer_array_get_item(daemons->apps, 0))) {
|
|
/* that's an error in the ess */
|
|
ORTE_ERROR_LOG(ORTE_ERR_NOT_FOUND);
|
|
return ORTE_ERR_NOT_FOUND;
|
|
}
|
|
orte_set_attribute(&dapp->attributes, ORTE_APP_PREFIX_DIR, ORTE_ATTR_LOCAL, param, OPAL_STRING);
|
|
|
|
lib_base = opal_basename(opal_install_dirs.libdir);
|
|
bin_base = opal_basename(opal_install_dirs.bindir);
|
|
|
|
/* Reset PATH */
|
|
newenv = opal_os_path( false, param, bin_base, NULL );
|
|
oldenv = getenv("PATH");
|
|
if (NULL != oldenv) {
|
|
char *temp;
|
|
asprintf(&temp, "%s:%s", newenv, oldenv );
|
|
free( newenv );
|
|
newenv = temp;
|
|
}
|
|
opal_setenv("PATH", newenv, true, &orte_launch_environ);
|
|
if (orte_debug_flag) {
|
|
opal_output(0, "%s: reset PATH: %s", orte_basename, newenv);
|
|
}
|
|
free(newenv);
|
|
free(bin_base);
|
|
|
|
/* Reset LD_LIBRARY_PATH */
|
|
newenv = opal_os_path( false, param, lib_base, NULL );
|
|
oldenv = getenv("LD_LIBRARY_PATH");
|
|
if (NULL != oldenv) {
|
|
char* temp;
|
|
asprintf(&temp, "%s:%s", newenv, oldenv);
|
|
free(newenv);
|
|
newenv = temp;
|
|
}
|
|
opal_setenv("LD_LIBRARY_PATH", newenv, true, &orte_launch_environ);
|
|
if (orte_debug_flag) {
|
|
opal_output(0, "%s: reset LD_LIBRARY_PATH: %s",
|
|
orte_basename, newenv);
|
|
}
|
|
free(newenv);
|
|
free(lib_base);
|
|
free(param);
|
|
}
|
|
|
|
/* setup for debugging */
|
|
orte_debugger_init_before_spawn(jdata);
|
|
|
|
rc = orte_plm.spawn(jdata);
|
|
} else {
|
|
/* flag that we are using the DVM */
|
|
orte_set_attribute(&jdata->attributes, ORTE_JOB_DVM_JOB, ORTE_ATTR_GLOBAL, NULL, OPAL_BOOL);
|
|
/* flag that the allocation is static - i.e., the DVM is not allowed
|
|
* to be adjusted once started, and all unused nodes are to be
|
|
* removed from the node pool */
|
|
orte_set_attribute(&jdata->attributes, ORTE_JOB_FIXED_DVM, ORTE_ATTR_GLOBAL, NULL, OPAL_BOOL);
|
|
// pack the ORTE_DAEMON_SPAWN_JOB_CMD command and job object and send to HNP at tag ORTE_RML_TAG_DAEMON
|
|
req = OBJ_NEW(opal_buffer_t);
|
|
if (OPAL_SUCCESS != (rc = opal_dss.pack(req, &cmd, 1, ORTE_DAEMON_CMD))) {
|
|
ORTE_ERROR_LOG(rc);
|
|
return rc;
|
|
}
|
|
if (OPAL_SUCCESS != (rc = opal_dss.pack(req, &jdata, 1, ORTE_JOB))) {
|
|
ORTE_ERROR_LOG(rc);
|
|
return rc;
|
|
}
|
|
if (OPAL_SUCCESS != (rc = opal_dss.pack(req, &trk->index, 1, OPAL_INT))) {
|
|
ORTE_ERROR_LOG(rc);
|
|
return rc;
|
|
}
|
|
orte_rml.send_buffer_nb(orte_mgmt_conduit,
|
|
ORTE_PROC_MY_HNP, req, ORTE_RML_TAG_DAEMON,
|
|
orte_rml_send_callback, NULL);
|
|
|
|
/* Inform the caller of the tracker index if they passed a index pointer */
|
|
if (NULL != index) {
|
|
*index = trk->index;
|
|
}
|
|
}
|
|
|
|
return ORTE_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
static int init_globals(void)
|
|
{
|
|
/* Reset the other fields every time */
|
|
orte_cmd_options.help = false;
|
|
orte_cmd_options.version = false;
|
|
orte_cmd_options.num_procs = 0;
|
|
if (NULL != orte_cmd_options.appfile) {
|
|
free(orte_cmd_options.appfile);
|
|
}
|
|
orte_cmd_options.appfile = NULL;
|
|
if (NULL != orte_cmd_options.wdir) {
|
|
free(orte_cmd_options.wdir);
|
|
}
|
|
orte_cmd_options.set_cwd_to_session_dir = false;
|
|
orte_cmd_options.wdir = NULL;
|
|
if (NULL != orte_cmd_options.path) {
|
|
free(orte_cmd_options.path);
|
|
}
|
|
orte_cmd_options.path = NULL;
|
|
|
|
orte_cmd_options.preload_binaries = false;
|
|
orte_cmd_options.preload_files = NULL;
|
|
|
|
/* All done */
|
|
return ORTE_SUCCESS;
|
|
}
|
|
|
|
|
|
static int parse_globals(int argc, char* argv[], opal_cmd_line_t *cmd_line)
|
|
{
|
|
/* check for request to report pid */
|
|
if (NULL != orte_cmd_options.report_pid) {
|
|
FILE *fp;
|
|
if (0 == strcmp(orte_cmd_options.report_pid, "-")) {
|
|
/* if '-', then output to stdout */
|
|
printf("%d\n", (int)getpid());
|
|
} else if (0 == strcmp(orte_cmd_options.report_pid, "+")) {
|
|
/* if '+', output to stderr */
|
|
fprintf(stderr, "%d\n", (int)getpid());
|
|
} else {
|
|
fp = fopen(orte_cmd_options.report_pid, "w");
|
|
if (NULL == fp) {
|
|
orte_show_help("help-orterun.txt", "orterun:write_file", false,
|
|
orte_basename, "pid", orte_cmd_options.report_pid);
|
|
exit(0);
|
|
}
|
|
fprintf(fp, "%d\n", (int)getpid());
|
|
fclose(fp);
|
|
}
|
|
}
|
|
|
|
/* Do we want a user-level debugger? */
|
|
|
|
if (orte_cmd_options.debugger) {
|
|
run_debugger(orte_basename, cmd_line, argc, argv, orte_cmd_options.num_procs);
|
|
}
|
|
|
|
return ORTE_SUCCESS;
|
|
}
|
|
|
|
|
|
static int parse_locals(orte_job_t *jdata, int argc, char* argv[])
|
|
{
|
|
int i, rc, app_num;
|
|
int temp_argc;
|
|
char **temp_argv, **env;
|
|
orte_app_context_t *app;
|
|
bool made_app;
|
|
orte_std_cntr_t j, size1;
|
|
|
|
/* Make the apps */
|
|
temp_argc = 0;
|
|
temp_argv = NULL;
|
|
opal_argv_append(&temp_argc, &temp_argv, argv[0]);
|
|
|
|
/* NOTE: This bogus env variable is necessary in the calls to
|
|
create_app(), below. See comment immediately before the
|
|
create_app() function for an explanation. */
|
|
|
|
env = NULL;
|
|
for (app_num = 0, i = 1; i < argc; ++i) {
|
|
if (0 == strcmp(argv[i], ":")) {
|
|
/* Make an app with this argv */
|
|
if (opal_argv_count(temp_argv) > 1) {
|
|
if (NULL != env) {
|
|
opal_argv_free(env);
|
|
env = NULL;
|
|
}
|
|
app = NULL;
|
|
rc = create_app(temp_argc, temp_argv, jdata, &app, &made_app, &env);
|
|
/** keep track of the number of apps - point this app_context to that index */
|
|
if (ORTE_SUCCESS != rc) {
|
|
/* Assume that the error message has already been
|
|
printed; no need to cleanup -- we can just
|
|
exit */
|
|
exit(1);
|
|
}
|
|
if (made_app) {
|
|
app->idx = app_num;
|
|
++app_num;
|
|
opal_pointer_array_add(jdata->apps, app);
|
|
++jdata->num_apps;
|
|
}
|
|
|
|
/* Reset the temps */
|
|
|
|
temp_argc = 0;
|
|
temp_argv = NULL;
|
|
opal_argv_append(&temp_argc, &temp_argv, argv[0]);
|
|
}
|
|
} else {
|
|
opal_argv_append(&temp_argc, &temp_argv, argv[i]);
|
|
}
|
|
}
|
|
|
|
if (opal_argv_count(temp_argv) > 1) {
|
|
app = NULL;
|
|
rc = create_app(temp_argc, temp_argv, jdata, &app, &made_app, &env);
|
|
if (ORTE_SUCCESS != rc) {
|
|
/* Assume that the error message has already been printed;
|
|
no need to cleanup -- we can just exit */
|
|
exit(1);
|
|
}
|
|
if (made_app) {
|
|
app->idx = app_num;
|
|
++app_num;
|
|
opal_pointer_array_add(jdata->apps, app);
|
|
++jdata->num_apps;
|
|
}
|
|
}
|
|
if (NULL != env) {
|
|
opal_argv_free(env);
|
|
}
|
|
opal_argv_free(temp_argv);
|
|
|
|
/* Once we've created all the apps, add the global MCA params to
|
|
each app's environment (checking for duplicates, of
|
|
course -- yay opal_environ_merge()). */
|
|
|
|
if (NULL != global_mca_env) {
|
|
size1 = (size_t)opal_pointer_array_get_size(jdata->apps);
|
|
/* Iterate through all the apps */
|
|
for (j = 0; j < size1; ++j) {
|
|
app = (orte_app_context_t *)
|
|
opal_pointer_array_get_item(jdata->apps, j);
|
|
if (NULL != app) {
|
|
/* Use handy utility function */
|
|
env = opal_environ_merge(global_mca_env, app->env);
|
|
opal_argv_free(app->env);
|
|
app->env = env;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Now take a subset of the MCA params and set them as MCA
|
|
overrides here in orterun (so that when we orte_init() later,
|
|
all the components see these MCA params). Here's how we decide
|
|
which subset of the MCA params we set here in orterun:
|
|
|
|
1. If any global MCA params were set, use those
|
|
2. If no global MCA params were set and there was only one app,
|
|
then use its app MCA params
|
|
3. Otherwise, don't set any
|
|
*/
|
|
|
|
env = NULL;
|
|
if (NULL != global_mca_env) {
|
|
env = global_mca_env;
|
|
} else {
|
|
if (opal_pointer_array_get_size(jdata->apps) >= 1) {
|
|
/* Remember that pointer_array's can be padded with NULL
|
|
entries; so only use the app's env if there is exactly
|
|
1 non-NULL entry */
|
|
app = (orte_app_context_t *)
|
|
opal_pointer_array_get_item(jdata->apps, 0);
|
|
if (NULL != app) {
|
|
env = app->env;
|
|
for (j = 1; j < opal_pointer_array_get_size(jdata->apps); ++j) {
|
|
if (NULL != opal_pointer_array_get_item(jdata->apps, j)) {
|
|
env = NULL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NULL != env) {
|
|
size1 = opal_argv_count(env);
|
|
for (j = 0; j < size1; ++j) {
|
|
/* Use-after-Free error possible here. putenv does not copy
|
|
* the string passed to it, and instead stores only the pointer.
|
|
* env[j] may be freed later, in which case the pointer
|
|
* in environ will now be left dangling into a deallocated
|
|
* region.
|
|
* So we make a copy of the variable.
|
|
*/
|
|
char *s = strdup(env[j]);
|
|
|
|
if (NULL == s) {
|
|
return OPAL_ERR_OUT_OF_RESOURCE;
|
|
}
|
|
putenv(s);
|
|
}
|
|
}
|
|
|
|
/* All done */
|
|
|
|
return ORTE_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
* This function takes a "char ***app_env" parameter to handle the
|
|
* specific case:
|
|
*
|
|
* orterun --mca foo bar -app appfile
|
|
*
|
|
* That is, we'll need to keep foo=bar, but the presence of the app
|
|
* file will cause an invocation of parse_appfile(), which will cause
|
|
* one or more recursive calls back to create_app(). Since the
|
|
* foo=bar value applies globally to all apps in the appfile, we need
|
|
* to pass in the "base" environment (that contains the foo=bar value)
|
|
* when we parse each line in the appfile.
|
|
*
|
|
* This is really just a special case -- when we have a simple case like:
|
|
*
|
|
* orterun --mca foo bar -np 4 hostname
|
|
*
|
|
* Then the upper-level function (parse_locals()) calls create_app()
|
|
* with a NULL value for app_env, meaning that there is no "base"
|
|
* environment that the app needs to be created from.
|
|
*/
|
|
static int create_app(int argc, char* argv[],
|
|
orte_job_t *jdata,
|
|
orte_app_context_t **app_ptr,
|
|
bool *made_app, char ***app_env)
|
|
{
|
|
char cwd[OPAL_PATH_MAX];
|
|
int i, j, count, rc;
|
|
char *param, *value;
|
|
orte_app_context_t *app = NULL;
|
|
bool found = false;
|
|
char *appname = NULL;
|
|
|
|
*made_app = false;
|
|
|
|
/* Pre-process the command line if we are going to parse an appfile later.
|
|
* save any mca command line args so they can be passed
|
|
* separately to the daemons.
|
|
* Use Case:
|
|
* $ cat launch.appfile
|
|
* -np 1 -mca aaa bbb ./my-app -mca ccc ddd
|
|
* -np 1 -mca aaa bbb ./my-app -mca eee fff
|
|
* $ mpirun -np 2 -mca foo bar --app launch.appfile
|
|
* Only pick up '-mca foo bar' on this pass.
|
|
*/
|
|
if (NULL != orte_cmd_options.appfile) {
|
|
if (ORTE_SUCCESS != (rc = orte_schizo.parse_cli(argc, 0, argv))) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
/* Parse application command line options. */
|
|
init_globals();
|
|
/* parse the cmd line - do this every time thru so we can
|
|
* repopulate the globals */
|
|
if (OPAL_SUCCESS != (rc = opal_cmd_line_parse(orte_cmd_line, true, false,
|
|
argc, argv)) ) {
|
|
if (OPAL_ERR_SILENT != rc) {
|
|
fprintf(stderr, "%s: command line error (%s)\n", argv[0],
|
|
opal_strerror(rc));
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/* Is there an appfile in here? */
|
|
if (NULL != orte_cmd_options.appfile) {
|
|
return parse_appfile(jdata, strdup(orte_cmd_options.appfile), app_env);
|
|
}
|
|
|
|
/* Setup application context */
|
|
app = OBJ_NEW(orte_app_context_t);
|
|
opal_cmd_line_get_tail(orte_cmd_line, &count, &app->argv);
|
|
|
|
/* See if we have anything left */
|
|
if (0 == count) {
|
|
orte_show_help("help-orterun.txt", "orterun:executable-not-specified",
|
|
true, orte_basename, orte_basename);
|
|
rc = ORTE_ERR_NOT_FOUND;
|
|
goto cleanup;
|
|
}
|
|
|
|
/*
|
|
* Get mca parameters so we can pass them to the daemons.
|
|
* Use the count determined above to make sure we do not go past
|
|
* the executable name. Example:
|
|
* mpirun -np 2 -mca foo bar ./my-app -mca bip bop
|
|
* We want to pick up '-mca foo bar' but not '-mca bip bop'
|
|
*/
|
|
if (ORTE_SUCCESS != (rc = orte_schizo.parse_cli(argc, count, argv))) {
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Grab all MCA environment variables */
|
|
|
|
app->env = opal_argv_copy(*app_env);
|
|
if (ORTE_SUCCESS != (rc = orte_schizo.parse_env(orte_cmd_options.path,
|
|
orte_cmd_line,
|
|
environ, &app->env))) {
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
/* Did the user request a specific wdir? */
|
|
|
|
if (NULL != orte_cmd_options.wdir) {
|
|
/* if this is a relative path, convert it to an absolute path */
|
|
if (opal_path_is_absolute(orte_cmd_options.wdir)) {
|
|
app->cwd = strdup(orte_cmd_options.wdir);
|
|
} else {
|
|
/* get the cwd */
|
|
if (OPAL_SUCCESS != (rc = opal_getcwd(cwd, sizeof(cwd)))) {
|
|
orte_show_help("help-orterun.txt", "orterun:init-failure",
|
|
true, "get the cwd", rc);
|
|
goto cleanup;
|
|
}
|
|
/* construct the absolute path */
|
|
app->cwd = opal_os_path(false, cwd, orte_cmd_options.wdir, NULL);
|
|
}
|
|
orte_set_attribute(&app->attributes, ORTE_APP_USER_CWD, ORTE_ATTR_GLOBAL, NULL, OPAL_BOOL);
|
|
} else if (orte_cmd_options.set_cwd_to_session_dir) {
|
|
orte_set_attribute(&app->attributes, ORTE_APP_SSNDIR_CWD, ORTE_ATTR_GLOBAL, NULL, OPAL_BOOL);
|
|
orte_set_attribute(&app->attributes, ORTE_APP_USER_CWD, ORTE_ATTR_GLOBAL, NULL, OPAL_BOOL);
|
|
} else {
|
|
if (OPAL_SUCCESS != (rc = opal_getcwd(cwd, sizeof(cwd)))) {
|
|
orte_show_help("help-orterun.txt", "orterun:init-failure",
|
|
true, "get the cwd", rc);
|
|
goto cleanup;
|
|
}
|
|
app->cwd = strdup(cwd);
|
|
}
|
|
|
|
/* if this is the first app_context, check for prefix directions.
|
|
* We only do this for the first app_context because the launchers
|
|
* only look at the first one when setting the prefix - we do NOT
|
|
* support per-app_context prefix settings!
|
|
*/
|
|
if (0 == total_num_apps) {
|
|
/* Check to see if the user explicitly wanted to disable automatic
|
|
--prefix behavior */
|
|
|
|
if (opal_cmd_line_is_taken(orte_cmd_line, "noprefix")) {
|
|
want_prefix_by_default = false;
|
|
}
|
|
|
|
/* Did the user specify a prefix, or want prefix by default? */
|
|
if (opal_cmd_line_is_taken(orte_cmd_line, "prefix") || want_prefix_by_default) {
|
|
size_t param_len;
|
|
/* if both the prefix was given and we have a prefix
|
|
* given above, check to see if they match
|
|
*/
|
|
if (opal_cmd_line_is_taken(orte_cmd_line, "prefix") &&
|
|
NULL != orte_cmd_options.prefix) {
|
|
/* if they don't match, then that merits a warning */
|
|
param = strdup(opal_cmd_line_get_param(orte_cmd_line, "prefix", 0, 0));
|
|
/* ensure we strip any trailing '/' */
|
|
if (0 == strcmp(OPAL_PATH_SEP, &(param[strlen(param)-1]))) {
|
|
param[strlen(param)-1] = '\0';
|
|
}
|
|
value = strdup(orte_cmd_options.prefix);
|
|
if (0 == strcmp(OPAL_PATH_SEP, &(value[strlen(value)-1]))) {
|
|
value[strlen(value)-1] = '\0';
|
|
}
|
|
if (0 != strcmp(param, value)) {
|
|
orte_show_help("help-orterun.txt", "orterun:app-prefix-conflict",
|
|
true, orte_basename, value, param);
|
|
/* let the global-level prefix take precedence since we
|
|
* know that one is being used
|
|
*/
|
|
free(param);
|
|
param = strdup(orte_cmd_options.prefix);
|
|
}
|
|
free(value);
|
|
} else if (NULL != orte_cmd_options.prefix) {
|
|
param = strdup(orte_cmd_options.prefix);
|
|
} else if (opal_cmd_line_is_taken(orte_cmd_line, "prefix")){
|
|
/* must be --prefix alone */
|
|
param = strdup(opal_cmd_line_get_param(orte_cmd_line, "prefix", 0, 0));
|
|
} else {
|
|
/* --enable-orterun-prefix-default was given to orterun */
|
|
param = strdup(opal_install_dirs.prefix);
|
|
}
|
|
|
|
if (NULL != param) {
|
|
/* "Parse" the param, aka remove superfluous path_sep. */
|
|
param_len = strlen(param);
|
|
while (0 == strcmp (OPAL_PATH_SEP, &(param[param_len-1]))) {
|
|
param[param_len-1] = '\0';
|
|
param_len--;
|
|
if (0 == param_len) {
|
|
orte_show_help("help-orterun.txt", "orterun:empty-prefix",
|
|
true, orte_basename, orte_basename);
|
|
free(param);
|
|
return ORTE_ERR_FATAL;
|
|
}
|
|
}
|
|
orte_set_attribute(&app->attributes, ORTE_APP_PREFIX_DIR, ORTE_ATTR_GLOBAL, param, OPAL_STRING);
|
|
free(param);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Did the user specify a hostfile. Need to check for both
|
|
* hostfile and machine file.
|
|
* We can only deal with one hostfile per app context, otherwise give an error.
|
|
*/
|
|
if (0 < (j = opal_cmd_line_get_ninsts(orte_cmd_line, "hostfile"))) {
|
|
if(1 < j) {
|
|
orte_show_help("help-orterun.txt", "orterun:multiple-hostfiles",
|
|
true, orte_basename, NULL);
|
|
return ORTE_ERR_FATAL;
|
|
} else {
|
|
value = opal_cmd_line_get_param(orte_cmd_line, "hostfile", 0, 0);
|
|
orte_set_attribute(&app->attributes, ORTE_APP_HOSTFILE, ORTE_ATTR_GLOBAL, value, OPAL_STRING);
|
|
}
|
|
}
|
|
if (0 < (j = opal_cmd_line_get_ninsts(orte_cmd_line, "machinefile"))) {
|
|
if(1 < j || orte_get_attribute(&app->attributes, ORTE_APP_HOSTFILE, NULL, OPAL_STRING)) {
|
|
orte_show_help("help-orterun.txt", "orterun:multiple-hostfiles",
|
|
true, orte_basename, NULL);
|
|
return ORTE_ERR_FATAL;
|
|
} else {
|
|
value = opal_cmd_line_get_param(orte_cmd_line, "machinefile", 0, 0);
|
|
orte_set_attribute(&app->attributes, ORTE_APP_HOSTFILE, ORTE_ATTR_GLOBAL, value, OPAL_STRING);
|
|
}
|
|
}
|
|
|
|
/* Did the user specify any hosts? */
|
|
if (0 < (j = opal_cmd_line_get_ninsts(orte_cmd_line, "host"))) {
|
|
char **targ=NULL, *tval;
|
|
for (i = 0; i < j; ++i) {
|
|
value = opal_cmd_line_get_param(orte_cmd_line, "host", i, 0);
|
|
opal_argv_append_nosize(&targ, value);
|
|
}
|
|
tval = opal_argv_join(targ, ',');
|
|
orte_set_attribute(&app->attributes, ORTE_APP_DASH_HOST, ORTE_ATTR_GLOBAL, tval, OPAL_STRING);
|
|
opal_argv_free(targ);
|
|
free(tval);
|
|
} else if (NULL != orte_default_dash_host) {
|
|
orte_set_attribute(&app->attributes, ORTE_APP_DASH_HOST, ORTE_ATTR_LOCAL,
|
|
orte_default_dash_host, OPAL_STRING);
|
|
}
|
|
|
|
/* check for bozo error */
|
|
if (0 > orte_cmd_options.num_procs) {
|
|
orte_show_help("help-orterun.txt", "orterun:negative-nprocs",
|
|
true, orte_basename, app->argv[0],
|
|
orte_cmd_options.num_procs, NULL);
|
|
return ORTE_ERR_FATAL;
|
|
}
|
|
|
|
app->num_procs = (orte_std_cntr_t)orte_cmd_options.num_procs;
|
|
total_num_apps++;
|
|
|
|
/* Capture any preload flags */
|
|
if (orte_cmd_options.preload_binaries) {
|
|
orte_set_attribute(&app->attributes, ORTE_APP_PRELOAD_BIN, ORTE_ATTR_GLOBAL, NULL, OPAL_BOOL);
|
|
}
|
|
/* if we were told to cwd to the session dir and the app was given in
|
|
* relative syntax, then we need to preload the binary to
|
|
* find the app - don't do this for java apps, however, as we
|
|
* can't easily find the class on the cmd line. Java apps have to
|
|
* preload their binary via the preload_files option
|
|
*/
|
|
if (!opal_path_is_absolute(app->argv[0]) &&
|
|
NULL == strstr(app->argv[0], "java")) {
|
|
if (orte_cmd_options.preload_binaries) {
|
|
orte_set_attribute(&app->attributes, ORTE_APP_SSNDIR_CWD, ORTE_ATTR_GLOBAL, NULL, OPAL_BOOL);
|
|
} else if (orte_get_attribute(&app->attributes, ORTE_APP_SSNDIR_CWD, NULL, OPAL_BOOL)) {
|
|
orte_set_attribute(&app->attributes, ORTE_APP_PRELOAD_BIN, ORTE_ATTR_GLOBAL, NULL, OPAL_BOOL);
|
|
}
|
|
}
|
|
if (NULL != orte_cmd_options.preload_files) {
|
|
orte_set_attribute(&app->attributes, ORTE_APP_PRELOAD_FILES, ORTE_ATTR_GLOBAL,
|
|
orte_cmd_options.preload_files, OPAL_STRING);
|
|
}
|
|
|
|
/* Do not try to find argv[0] here -- the starter is responsible
|
|
for that because it may not be relevant to try to find it on
|
|
the node where orterun is executing. So just strdup() argv[0]
|
|
into app. */
|
|
|
|
app->app = strdup(app->argv[0]);
|
|
if (NULL == app->app) {
|
|
orte_show_help("help-orterun.txt", "orterun:call-failed",
|
|
true, orte_basename, "library", "strdup returned NULL", errno);
|
|
rc = ORTE_ERR_NOT_FOUND;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* if this is a Java application, we have a bit more work to do. Such
|
|
* applications actually need to be run under the Java virtual machine
|
|
* and the "java" command will start the "executable". So we need to ensure
|
|
* that all the proper java-specific paths are provided
|
|
*/
|
|
appname = opal_basename(app->app);
|
|
if (0 == strcmp(appname, "java")) {
|
|
/* see if we were given a library path */
|
|
found = false;
|
|
for (i=1; NULL != app->argv[i]; i++) {
|
|
if (NULL != strstr(app->argv[i], "java.library.path")) {
|
|
char *dptr;
|
|
/* find the '=' that delineates the option from the path */
|
|
if (NULL == (dptr = strchr(app->argv[i], '='))) {
|
|
/* that's just wrong */
|
|
rc = ORTE_ERR_BAD_PARAM;
|
|
goto cleanup;
|
|
}
|
|
/* step over the '=' */
|
|
++dptr;
|
|
/* yep - but does it include the path to the mpi libs? */
|
|
found = true;
|
|
if (NULL == strstr(app->argv[i], opal_install_dirs.libdir)) {
|
|
/* doesn't appear to - add it to be safe */
|
|
if (':' == app->argv[i][strlen(app->argv[i]-1)]) {
|
|
asprintf(&value, "-Djava.library.path=%s%s", dptr, opal_install_dirs.libdir);
|
|
} else {
|
|
asprintf(&value, "-Djava.library.path=%s:%s", dptr, opal_install_dirs.libdir);
|
|
}
|
|
free(app->argv[i]);
|
|
app->argv[i] = value;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
/* need to add it right after the java command */
|
|
asprintf(&value, "-Djava.library.path=%s", opal_install_dirs.libdir);
|
|
opal_argv_insert_element(&app->argv, 1, value);
|
|
free(value);
|
|
}
|
|
|
|
/* see if we were given a class path */
|
|
found = false;
|
|
for (i=1; NULL != app->argv[i]; i++) {
|
|
if (NULL != strstr(app->argv[i], "cp") ||
|
|
NULL != strstr(app->argv[i], "classpath")) {
|
|
/* yep - but does it include the path to the mpi libs? */
|
|
found = true;
|
|
/* check if mpi.jar exists - if so, add it */
|
|
value = opal_os_path(false, opal_install_dirs.libdir, "mpi.jar", NULL);
|
|
if (access(value, F_OK ) != -1) {
|
|
set_classpath_jar_file(app, i+1, "mpi.jar");
|
|
}
|
|
free(value);
|
|
/* check for oshmem support */
|
|
value = opal_os_path(false, opal_install_dirs.libdir, "shmem.jar", NULL);
|
|
if (access(value, F_OK ) != -1) {
|
|
set_classpath_jar_file(app, i+1, "shmem.jar");
|
|
}
|
|
free(value);
|
|
/* always add the local directory */
|
|
asprintf(&value, "%s:%s", app->cwd, app->argv[i+1]);
|
|
free(app->argv[i+1]);
|
|
app->argv[i+1] = value;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
/* check to see if CLASSPATH is in the environment */
|
|
found = false; // just to be pedantic
|
|
for (i=0; NULL != environ[i]; i++) {
|
|
if (0 == strncmp(environ[i], "CLASSPATH", strlen("CLASSPATH"))) {
|
|
value = strchr(environ[i], '=');
|
|
++value; /* step over the = */
|
|
opal_argv_insert_element(&app->argv, 1, value);
|
|
/* check for mpi.jar */
|
|
value = opal_os_path(false, opal_install_dirs.libdir, "mpi.jar", NULL);
|
|
if (access(value, F_OK ) != -1) {
|
|
set_classpath_jar_file(app, 1, "mpi.jar");
|
|
}
|
|
free(value);
|
|
/* check for shmem.jar */
|
|
value = opal_os_path(false, opal_install_dirs.libdir, "shmem.jar", NULL);
|
|
if (access(value, F_OK ) != -1) {
|
|
set_classpath_jar_file(app, 1, "shmem.jar");
|
|
}
|
|
free(value);
|
|
/* always add the local directory */
|
|
(void)asprintf(&value, "%s:%s", app->cwd, app->argv[1]);
|
|
free(app->argv[1]);
|
|
app->argv[1] = value;
|
|
opal_argv_insert_element(&app->argv, 1, "-cp");
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
/* need to add it right after the java command - have
|
|
* to include the working directory and trust that
|
|
* the user set cwd if necessary
|
|
*/
|
|
char *str, *str2;
|
|
/* always start with the working directory */
|
|
str = strdup(app->cwd);
|
|
/* check for mpi.jar */
|
|
value = opal_os_path(false, opal_install_dirs.libdir, "mpi.jar", NULL);
|
|
if (access(value, F_OK ) != -1) {
|
|
(void)asprintf(&str2, "%s:%s", str, value);
|
|
free(str);
|
|
str = str2;
|
|
}
|
|
free(value);
|
|
/* check for shmem.jar */
|
|
value = opal_os_path(false, opal_install_dirs.libdir, "shmem.jar", NULL);
|
|
if (access(value, F_OK ) != -1) {
|
|
asprintf(&str2, "%s:%s", str, value);
|
|
free(str);
|
|
str = str2;
|
|
}
|
|
free(value);
|
|
opal_argv_insert_element(&app->argv, 1, str);
|
|
free(str);
|
|
opal_argv_insert_element(&app->argv, 1, "-cp");
|
|
}
|
|
}
|
|
/* try to find the actual command - may not be perfect */
|
|
for (i=1; i < opal_argv_count(app->argv); i++) {
|
|
if (NULL != strstr(app->argv[i], "java.library.path")) {
|
|
continue;
|
|
} else if (NULL != strstr(app->argv[i], "cp") ||
|
|
NULL != strstr(app->argv[i], "classpath")) {
|
|
/* skip the next field */
|
|
i++;
|
|
continue;
|
|
}
|
|
/* declare this the winner */
|
|
opal_setenv("OMPI_COMMAND", app->argv[i], true, &app->env);
|
|
/* collect everything else as the cmd line */
|
|
if ((i+1) < opal_argv_count(app->argv)) {
|
|
value = opal_argv_join(&app->argv[i+1], ' ');
|
|
opal_setenv("OMPI_ARGV", value, true, &app->env);
|
|
free(value);
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
/* add the cmd to the environment for MPI_Info to pickup */
|
|
opal_setenv("OMPI_COMMAND", appname, true, &app->env);
|
|
if (1 < opal_argv_count(app->argv)) {
|
|
value = opal_argv_join(&app->argv[1], ' ');
|
|
opal_setenv("OMPI_ARGV", value, true, &app->env);
|
|
free(value);
|
|
}
|
|
}
|
|
|
|
*app_ptr = app;
|
|
app = NULL;
|
|
*made_app = true;
|
|
|
|
/* All done */
|
|
|
|
cleanup:
|
|
if (NULL != app) {
|
|
OBJ_RELEASE(app);
|
|
}
|
|
if (NULL != appname) {
|
|
free(appname);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static void set_classpath_jar_file(orte_app_context_t *app, int index, char *jarfile)
|
|
{
|
|
if (NULL == strstr(app->argv[index], jarfile)) {
|
|
/* nope - need to add it */
|
|
char *fmt = ':' == app->argv[index][strlen(app->argv[index]-1)]
|
|
? "%s%s/%s" : "%s:%s/%s";
|
|
char *str;
|
|
asprintf(&str, fmt, app->argv[index], opal_install_dirs.libdir, jarfile);
|
|
free(app->argv[index]);
|
|
app->argv[index] = str;
|
|
}
|
|
}
|
|
|
|
static int parse_appfile(orte_job_t *jdata, char *filename, char ***env)
|
|
{
|
|
size_t i, len;
|
|
FILE *fp;
|
|
char line[BUFSIZ];
|
|
int rc, argc, app_num;
|
|
char **argv;
|
|
orte_app_context_t *app;
|
|
bool blank, made_app;
|
|
char bogus[] = "bogus ";
|
|
char **tmp_env;
|
|
|
|
/*
|
|
* Make sure to clear out this variable so we don't do anything odd in
|
|
* app_create()
|
|
*/
|
|
if (NULL != orte_cmd_options.appfile) {
|
|
free(orte_cmd_options.appfile);
|
|
orte_cmd_options.appfile = NULL;
|
|
}
|
|
|
|
/* Try to open the file */
|
|
|
|
fp = fopen(filename, "r");
|
|
if (NULL == fp) {
|
|
orte_show_help("help-orterun.txt", "orterun:appfile-not-found", true,
|
|
filename);
|
|
return ORTE_ERR_NOT_FOUND;
|
|
}
|
|
|
|
/* Read in line by line */
|
|
|
|
line[sizeof(line) - 1] = '\0';
|
|
app_num = 0;
|
|
do {
|
|
|
|
/* We need a bogus argv[0] (because when argv comes in from
|
|
the command line, argv[0] is "orterun", so the parsing
|
|
logic ignores it). So create one here rather than making
|
|
an argv and then pre-pending a new argv[0] (which would be
|
|
rather inefficient). */
|
|
|
|
line[0] = '\0';
|
|
strcat(line, bogus);
|
|
|
|
if (NULL == fgets(line + sizeof(bogus) - 1,
|
|
sizeof(line) - sizeof(bogus) - 1, fp)) {
|
|
break;
|
|
}
|
|
|
|
/* Remove a trailing newline */
|
|
|
|
len = strlen(line);
|
|
if (len > 0 && '\n' == line[len - 1]) {
|
|
line[len - 1] = '\0';
|
|
if (len > 0) {
|
|
--len;
|
|
}
|
|
}
|
|
|
|
/* Remove comments */
|
|
|
|
for (i = 0; i < len; ++i) {
|
|
if ('#' == line[i]) {
|
|
line[i] = '\0';
|
|
break;
|
|
} else if (i + 1 < len && '/' == line[i] && '/' == line[i + 1]) {
|
|
line[i] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Is this a blank line? */
|
|
|
|
len = strlen(line);
|
|
for (blank = true, i = sizeof(bogus); i < len; ++i) {
|
|
if (!isspace(line[i])) {
|
|
blank = false;
|
|
break;
|
|
}
|
|
}
|
|
if (blank) {
|
|
continue;
|
|
}
|
|
|
|
/* We got a line with *something* on it. So process it */
|
|
|
|
argv = opal_argv_split(line, ' ');
|
|
argc = opal_argv_count(argv);
|
|
if (argc > 0) {
|
|
|
|
/* Create a temporary env to use in the recursive call --
|
|
that is: don't disturb the original env so that we can
|
|
have a consistent global env. This allows for the
|
|
case:
|
|
|
|
orterun --mca foo bar --appfile file
|
|
|
|
where the "file" contains multiple apps. In this case,
|
|
each app in "file" will get *only* foo=bar as the base
|
|
environment from which its specific environment is
|
|
constructed. */
|
|
|
|
if (NULL != *env) {
|
|
tmp_env = opal_argv_copy(*env);
|
|
if (NULL == tmp_env) {
|
|
fclose(fp);
|
|
opal_argv_free(argv);
|
|
return ORTE_ERR_OUT_OF_RESOURCE;
|
|
}
|
|
} else {
|
|
tmp_env = NULL;
|
|
}
|
|
|
|
rc = create_app(argc, argv, jdata, &app, &made_app, &tmp_env);
|
|
if (ORTE_SUCCESS != rc) {
|
|
/* Assume that the error message has already been
|
|
printed; no need to cleanup -- we can just exit */
|
|
exit(1);
|
|
}
|
|
if (NULL != tmp_env) {
|
|
opal_argv_free(tmp_env);
|
|
}
|
|
if (made_app) {
|
|
app->idx = app_num;
|
|
++app_num;
|
|
opal_pointer_array_add(jdata->apps, app);
|
|
++jdata->num_apps;
|
|
}
|
|
}
|
|
opal_argv_free(argv);
|
|
} while (!feof(fp));
|
|
fclose(fp);
|
|
|
|
/* All done */
|
|
|
|
free(filename);
|
|
|
|
return ORTE_SUCCESS;
|
|
}
|
|
|
|
static void launch_recv(int status, orte_process_name_t* sender,
|
|
opal_buffer_t *buffer,
|
|
orte_rml_tag_t tag, void *cbdata)
|
|
{
|
|
int rc;
|
|
int32_t ret;
|
|
int32_t cnt;
|
|
orte_jobid_t jobid;
|
|
orte_app_context_t *app;
|
|
orte_proc_t *proc;
|
|
orte_node_t *node;
|
|
int tool_job_index;
|
|
trackr_t *trk;
|
|
|
|
/* unpack the completion status */
|
|
cnt = 1;
|
|
if (OPAL_SUCCESS != (rc = opal_dss.unpack(buffer, &ret, &cnt, OPAL_INT32))) {
|
|
ORTE_ERROR_LOG(rc);
|
|
ORTE_UPDATE_EXIT_STATUS(rc);
|
|
return;
|
|
}
|
|
/* update our exit status to match */
|
|
ORTE_UPDATE_EXIT_STATUS(ret);
|
|
|
|
/* unpack the jobid */
|
|
cnt = 1;
|
|
if (OPAL_SUCCESS != (rc = opal_dss.unpack(buffer, &jobid, &cnt, ORTE_JOBID))) {
|
|
ORTE_ERROR_LOG(rc);
|
|
ORTE_UPDATE_EXIT_STATUS(rc);
|
|
return;
|
|
}
|
|
|
|
/* unpack our tracking id */
|
|
cnt = 1;
|
|
if (OPAL_SUCCESS != (rc = opal_dss.unpack(buffer, &tool_job_index, &cnt, OPAL_INT))) {
|
|
ORTE_ERROR_LOG(rc);
|
|
ORTE_UPDATE_EXIT_STATUS(rc);
|
|
return;
|
|
}
|
|
|
|
// Store the job id in the job data
|
|
if (NULL == (trk = (trackr_t*)opal_pointer_array_get_item(&tool_jobs, tool_job_index))) {
|
|
opal_output(0, "SPAWN OF TRACKER ID %d RETURNED INDEX TO NULL OBJECT", tool_job_index);
|
|
return;
|
|
}
|
|
trk->jdata->jobid = jobid;
|
|
|
|
if (ORTE_SUCCESS == ret) {
|
|
printf("[ORTE] Task: %d is launched! (Job ID: %s)\n", tool_job_index, ORTE_JOBID_PRINT(jobid));
|
|
} else {
|
|
/* unpack the offending proc and node, if sent */
|
|
cnt = 1;
|
|
if (OPAL_SUCCESS == opal_dss.unpack(buffer, &trk->jdata->state, &cnt, ORTE_JOB_STATE_T)) {
|
|
cnt = 1;
|
|
opal_dss.unpack(buffer, &proc, &cnt, ORTE_PROC);
|
|
proc->exit_code = ret;
|
|
app = (orte_app_context_t*)opal_pointer_array_get_item(trk->jdata->apps, proc->app_idx);
|
|
cnt = 1;
|
|
opal_dss.unpack(buffer, &node, &cnt, ORTE_NODE);
|
|
orte_print_aborted_job(trk->jdata, app, proc, node);
|
|
}
|
|
}
|
|
|
|
/* Inform client */
|
|
if (NULL != trk->launch_cb) {
|
|
trk->launch_cb(tool_job_index, trk->jdata, ret, trk->launch_cbdata);
|
|
}
|
|
|
|
/* if the job failed to launch, then we remove the tracker */
|
|
if (ORTE_SUCCESS != ret) {
|
|
opal_pointer_array_set_item(&tool_jobs, tool_job_index, NULL);
|
|
OBJ_RELEASE(trk);
|
|
}
|
|
}
|
|
|
|
static void complete_recv(int status, orte_process_name_t* sender,
|
|
opal_buffer_t *buffer,
|
|
orte_rml_tag_t tag, void *cbdata)
|
|
{
|
|
int rc, ret;
|
|
int32_t cnt;
|
|
orte_jobid_t jobid;
|
|
orte_app_context_t *app;
|
|
orte_proc_t *proc;
|
|
orte_node_t *node;
|
|
int tool_job_index;
|
|
trackr_t *trk;
|
|
|
|
/* unpack the completion status */
|
|
cnt = 1;
|
|
if (OPAL_SUCCESS != (rc = opal_dss.unpack(buffer, &ret, &cnt, OPAL_INT))) {
|
|
ORTE_ERROR_LOG(rc);
|
|
ORTE_UPDATE_EXIT_STATUS(rc);
|
|
return;
|
|
}
|
|
|
|
/* unpack the jobid */
|
|
cnt = 1;
|
|
if (OPAL_SUCCESS != (rc = opal_dss.unpack(buffer, &jobid, &cnt, ORTE_JOBID))) {
|
|
ORTE_ERROR_LOG(rc);
|
|
ORTE_UPDATE_EXIT_STATUS(rc);
|
|
return;
|
|
}
|
|
|
|
/* unpack our tracking id */
|
|
cnt = 1;
|
|
if (OPAL_SUCCESS != (rc = opal_dss.unpack(buffer, &tool_job_index, &cnt, OPAL_INT))) {
|
|
ORTE_ERROR_LOG(rc);
|
|
ORTE_UPDATE_EXIT_STATUS(rc);
|
|
return;
|
|
}
|
|
|
|
/* get the tracker */
|
|
if (NULL == (trk = (trackr_t*)opal_pointer_array_get_item(&tool_jobs, tool_job_index))) {
|
|
opal_output(0, "TRACKER ID %d RETURNED INDEX TO NULL OBJECT", tool_job_index);
|
|
return;
|
|
}
|
|
|
|
if (ORTE_SUCCESS == ret) {
|
|
printf("[ORTE] Task: %d returned: %d (Job ID: %s)\n", tool_job_index, ret, ORTE_JOBID_PRINT(jobid));
|
|
} else {
|
|
/* unpack the offending proc and node */
|
|
cnt = 1;
|
|
opal_dss.unpack(buffer, &trk->jdata->state, &cnt, ORTE_JOB_STATE_T);
|
|
cnt = 1;
|
|
opal_dss.unpack(buffer, &proc, &cnt, ORTE_PROC);
|
|
proc->exit_code = ret;
|
|
app = (orte_app_context_t*)opal_pointer_array_get_item(trk->jdata->apps, proc->app_idx);
|
|
cnt = 1;
|
|
opal_dss.unpack(buffer, &node, &cnt, ORTE_NODE);
|
|
orte_print_aborted_job(trk->jdata, app, proc, node);
|
|
}
|
|
|
|
/* Inform client */
|
|
if (NULL != trk && NULL != trk->complete_cb) {
|
|
trk->complete_cb(tool_job_index, trk->jdata, ret, trk->complete_cbdata);
|
|
}
|
|
/* cleanup */
|
|
opal_pointer_array_set_item(&tool_jobs, tool_job_index, NULL);
|
|
OBJ_RELEASE(trk);
|
|
}
|
|
|
|
|
|
/**** DEBUGGER CODE ****/
|
|
/*
|
|
* Debugger support for orterun
|
|
*
|
|
* We interpret the MPICH debugger interface as follows:
|
|
*
|
|
* a) The launcher
|
|
* - spawns the other processes,
|
|
* - fills in the table MPIR_proctable, and sets MPIR_proctable_size
|
|
* - sets MPIR_debug_state to MPIR_DEBUG_SPAWNED ( = 1)
|
|
* - calls MPIR_Breakpoint() which the debugger will have a
|
|
* breakpoint on.
|
|
*
|
|
* b) Applications start and then spin until MPIR_debug_gate is set
|
|
* non-zero by the debugger.
|
|
*
|
|
* This file implements (a).
|
|
*
|
|
**************************************************************************
|
|
*
|
|
* Note that we have presently tested both TotalView and DDT parallel
|
|
* debuggers. They both nominally subscribe to the Etnus attaching
|
|
* interface, but there are differences between the two.
|
|
*
|
|
* TotalView: user launches "totalview mpirun -a ...<mpirun args>...".
|
|
* TV launches mpirun. mpirun launches the application and then calls
|
|
* MPIR_Breakpoint(). This is the signal to TV that it's a parallel
|
|
* MPI job. TV then reads the proctable in mpirun and attaches itself
|
|
* to all the processes (it takes care of launching itself on the
|
|
* remote nodes). Upon attaching to all the MPI processes, the
|
|
* variable MPIR_being_debugged is set to 1. When it has finished
|
|
* attaching itself to all the MPI processes that it wants to,
|
|
* MPIR_Breakpoint() returns.
|
|
*
|
|
* DDT: user launches "ddt bin -np X <mpi app name>". DDT fork/exec's
|
|
* mpirun to launch ddt-debugger on the back-end nodes via "mpirun -np
|
|
* X ddt-debugger" (not the lack of other arguments -- we can't pass
|
|
* anything to mpirun). This app will eventually fork/exec the MPI
|
|
* app. DDT does not current set MPIR_being_debugged in the MPI app.
|
|
*
|
|
**************************************************************************
|
|
*
|
|
* We support two ways of waiting for attaching debuggers. The
|
|
* implementation spans this file and ompi/debuggers/ompi_debuggers.c.
|
|
*
|
|
* 1. If using orterun: MPI processes will have the
|
|
* orte_in_parallel_debugger MCA param set to true (because not all
|
|
* debuggers consistently set MPIR_being_debugged in both the launcher
|
|
* and in the MPI procs). The HNP will call MPIR_Breakpoint() and
|
|
* then RML send a message to VPID 0 (MCW rank 0) when it returns
|
|
* (MPIR_Breakpoint() doesn't return until the debugger has attached
|
|
* to all relevant processes). Meanwhile, VPID 0 blocks waiting for
|
|
* the RML message. All other VPIDs immediately call the grpcomm
|
|
* barrier (and therefore block until the debugger attaches). Once
|
|
* VPID 0 receives the RML message, we know that the debugger has
|
|
* attached to all processes that it cares about, and VPID 0 then
|
|
* joins the grpcomm barrier, allowing the job to continue. This
|
|
* scheme has the side effect of nicely supporting partial attaches by
|
|
* parallel debuggers (i.e., attaching to only some of the MPI
|
|
* processes; not necessarily all of them).
|
|
*
|
|
* 2. If not using orterun: in this case, we know that there will not be an RML message
|
|
* sent to VPID 0. So we have to look for a magic environment
|
|
* variable from the launcher to know if the jobs will be attached by
|
|
* a debugger (e.g., set by yod, srun, ...etc.), and if so, spin on
|
|
* MPIR_debug_gate. These environment variable names must be
|
|
* hard-coded in the OMPI layer (see ompi/debuggers/ompi_debuggers.c).
|
|
*/
|
|
|
|
/* local globals and functions */
|
|
#define DUMP_INT(X) fprintf(stderr, " %s = %d\n", # X, X);
|
|
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
|
|
|
|
struct MPIR_PROCDESC {
|
|
char *host_name; /* something that can be passed to inet_addr */
|
|
char *executable_name; /* name of binary */
|
|
int pid; /* process pid */
|
|
};
|
|
|
|
|
|
/**
|
|
* Initialization of data structures for running under a debugger
|
|
* using the MPICH/TotalView parallel debugger interface. Before the
|
|
* spawn we need to check if we are being run under a TotalView-like
|
|
* debugger; if so then inform applications via an MCA parameter.
|
|
*/
|
|
static void orte_debugger_init_before_spawn(orte_job_t *jdata)
|
|
{
|
|
char *env_name;
|
|
orte_app_context_t *app;
|
|
int i;
|
|
char *attach_fifo;
|
|
|
|
if (!MPIR_being_debugged && !orte_in_parallel_debugger) {
|
|
/* if we were given a test debugger, then we still want to
|
|
* colaunch it - unless we are testing attach to a running job
|
|
*/
|
|
if (NULL != orte_debugger_test_daemon && !orte_debugger_test_attach) {
|
|
opal_output_verbose(2, orte_debug_output,
|
|
"%s Debugger test daemon specified: %s",
|
|
ORTE_NAME_PRINT(ORTE_PROC_MY_NAME),
|
|
orte_debugger_test_daemon);
|
|
goto launchit;
|
|
}
|
|
/* if we were given an auto-detect rate, then we want to setup
|
|
* an event so we periodically do the check
|
|
*/
|
|
if (0 < orte_debugger_check_rate) {
|
|
opal_output_verbose(2, orte_debug_output,
|
|
"%s Setting debugger attach check rate for %d seconds",
|
|
ORTE_NAME_PRINT(ORTE_PROC_MY_NAME),
|
|
orte_debugger_check_rate);
|
|
ORTE_TIMER_EVENT(orte_debugger_check_rate, 0, attach_debugger, ORTE_SYS_PRI);
|
|
} else if (orte_create_session_dirs) {
|
|
/* create the attachment FIFO and setup readevent - cannot be
|
|
* done if no session dirs exist!
|
|
*/
|
|
attach_fifo = opal_os_path(false, orte_process_info.job_session_dir,
|
|
"debugger_attach_fifo", NULL);
|
|
if ((mkfifo(attach_fifo, FILE_MODE) < 0) && errno != EEXIST) {
|
|
opal_output(0, "CANNOT CREATE FIFO %s: errno %d", attach_fifo, errno);
|
|
free(attach_fifo);
|
|
return;
|
|
}
|
|
strncpy(MPIR_attach_fifo, attach_fifo, MPIR_MAX_PATH_LENGTH - 1);
|
|
free(attach_fifo);
|
|
open_fifo();
|
|
}
|
|
return;
|
|
}
|
|
|
|
launchit:
|
|
opal_output_verbose(1, orte_debug_output, "Info: Spawned by a debugger");
|
|
|
|
/* tell the procs they are being debugged */
|
|
(void) mca_base_var_env_name ("orte_in_parallel_debugger", &env_name);
|
|
|
|
for (i=0; i < jdata->apps->size; i++) {
|
|
if (NULL == (app = (orte_app_context_t*)opal_pointer_array_get_item(jdata->apps, i))) {
|
|
continue;
|
|
}
|
|
opal_setenv(env_name, "1", true, &app->env);
|
|
}
|
|
free(env_name);
|
|
}
|
|
|
|
static bool mpir_breakpoint_fired = false;
|
|
|
|
static void _send_notification(void)
|
|
{
|
|
opal_buffer_t buf;
|
|
int status = OPAL_ERR_DEBUGGER_RELEASE;
|
|
orte_grpcomm_signature_t sig;
|
|
int rc;
|
|
opal_value_t kv, *kvptr;
|
|
|
|
OBJ_CONSTRUCT(&buf, opal_buffer_t);
|
|
|
|
/* pack the debugger_attached status */
|
|
if (ORTE_SUCCESS != (rc = opal_dss.pack(&buf, &status, 1, OPAL_INT))) {
|
|
ORTE_ERROR_LOG(rc);
|
|
OBJ_DESTRUCT(&buf);
|
|
return;
|
|
}
|
|
|
|
/* the source is me */
|
|
if (ORTE_SUCCESS != (rc = opal_dss.pack(&buf, ORTE_PROC_MY_NAME, 1, ORTE_NAME))) {
|
|
ORTE_ERROR_LOG(rc);
|
|
OBJ_DESTRUCT(&buf);
|
|
return;
|
|
}
|
|
|
|
/* instruct that it is to go only to non-default evhandlers */
|
|
status = 1;
|
|
if (ORTE_SUCCESS != (rc = opal_dss.pack(&buf, &status, 1, OPAL_INT))) {
|
|
ORTE_ERROR_LOG(rc);
|
|
OBJ_DESTRUCT(&buf);
|
|
return;
|
|
}
|
|
OBJ_CONSTRUCT(&kv, opal_value_t);
|
|
kv.key = strdup(OPAL_PMIX_EVENT_NON_DEFAULT);
|
|
kv.type = OPAL_BOOL;
|
|
kv.data.flag = true;
|
|
kvptr = &kv;
|
|
if (ORTE_SUCCESS != (rc = opal_dss.pack(&buf, &kvptr, 1, OPAL_VALUE))) {
|
|
ORTE_ERROR_LOG(rc);
|
|
OBJ_DESTRUCT(&kv);
|
|
OBJ_DESTRUCT(&buf);
|
|
return;
|
|
}
|
|
OBJ_DESTRUCT(&kv);
|
|
|
|
/* xcast it to everyone */
|
|
OBJ_CONSTRUCT(&sig, orte_grpcomm_signature_t);
|
|
sig.signature = (orte_process_name_t*)malloc(sizeof(orte_process_name_t));
|
|
sig.signature[0].jobid = ORTE_PROC_MY_NAME->jobid;
|
|
sig.signature[0].vpid = ORTE_VPID_WILDCARD;
|
|
sig.sz = 1;
|
|
|
|
if (ORTE_SUCCESS != (rc = orte_grpcomm.xcast(&sig, ORTE_RML_TAG_NOTIFICATION, &buf))) {
|
|
ORTE_ERROR_LOG(rc);
|
|
}
|
|
OBJ_DESTRUCT(&sig);
|
|
OBJ_DESTRUCT(&buf);
|
|
}
|
|
|
|
static void orte_debugger_dump(void)
|
|
{
|
|
int i;
|
|
|
|
DUMP_INT(MPIR_being_debugged);
|
|
DUMP_INT(MPIR_debug_state);
|
|
DUMP_INT(MPIR_partial_attach_ok);
|
|
DUMP_INT(MPIR_i_am_starter);
|
|
DUMP_INT(MPIR_forward_output);
|
|
DUMP_INT(MPIR_proctable_size);
|
|
fprintf(stderr, " MPIR_proctable:\n");
|
|
for (i = 0; i < MPIR_proctable_size; i++) {
|
|
fprintf(stderr,
|
|
" (i, host, exe, pid) = (%d, %s, %s, %d)\n",
|
|
i,
|
|
MPIR_proctable[i].host_name,
|
|
MPIR_proctable[i].executable_name,
|
|
MPIR_proctable[i].pid);
|
|
}
|
|
fprintf(stderr, "MPIR_executable_path: %s\n",
|
|
('\0' == MPIR_executable_path[0]) ?
|
|
"NULL" : (char*) MPIR_executable_path);
|
|
fprintf(stderr, "MPIR_server_arguments: %s\n",
|
|
('\0' == MPIR_server_arguments[0]) ?
|
|
"NULL" : (char*) MPIR_server_arguments);
|
|
}
|
|
|
|
static void setup_debugger_job(void)
|
|
{
|
|
orte_job_t *debugger;
|
|
orte_app_context_t *app;
|
|
orte_proc_t *proc;
|
|
int i, rc;
|
|
orte_node_t *node;
|
|
orte_vpid_t vpid=0;
|
|
char cwd[OPAL_PATH_MAX];
|
|
|
|
/* setup debugger daemon job */
|
|
debugger = OBJ_NEW(orte_job_t);
|
|
/* create a jobid for these daemons - this is done solely
|
|
* to avoid confusing the rest of the system's bookkeeping
|
|
*/
|
|
orte_plm_base_create_jobid(debugger);
|
|
/* set the personality to ORTE */
|
|
opal_argv_append_nosize(&debugger->personality, "orte");
|
|
/* flag the job as being debugger daemons */
|
|
ORTE_FLAG_SET(debugger, ORTE_JOB_FLAG_DEBUGGER_DAEMON);
|
|
/* unless directed, we do not forward output */
|
|
if (!MPIR_forward_output) {
|
|
ORTE_FLAG_SET(debugger, ORTE_JOB_FLAG_FORWARD_OUTPUT);
|
|
}
|
|
/* dont push stdin */
|
|
debugger->stdin_target = ORTE_VPID_INVALID;
|
|
/* add it to the global job pool */
|
|
opal_hash_table_set_value_uint32(orte_job_data, debugger->jobid, debugger);
|
|
/* create an app_context for the debugger daemon */
|
|
app = OBJ_NEW(orte_app_context_t);
|
|
if (NULL != orte_debugger_test_daemon) {
|
|
app->app = strdup(orte_debugger_test_daemon);
|
|
} else {
|
|
app->app = strdup((char*)MPIR_executable_path);
|
|
}
|
|
/* don't currently have an option to pass the debugger
|
|
* cwd - probably should add one someday
|
|
*/
|
|
if (OPAL_SUCCESS != (rc = opal_getcwd(cwd, sizeof(cwd)))) {
|
|
orte_show_help("help-orterun.txt", "orterun:init-failure",
|
|
true, "get the cwd", rc);
|
|
return;
|
|
}
|
|
app->cwd = strdup(cwd);
|
|
orte_remove_attribute(&app->attributes, ORTE_APP_USER_CWD);
|
|
opal_argv_append_nosize(&app->argv, app->app);
|
|
build_debugger_args(app);
|
|
opal_pointer_array_add(debugger->apps, app);
|
|
debugger->num_apps = 1;
|
|
/* create a job map */
|
|
debugger->map = OBJ_NEW(orte_job_map_t);
|
|
/* in building the map, we want to launch one debugger daemon
|
|
* on each node that *already has an application process on it*.
|
|
* We cannot just launch one debugger daemon on EVERY node because
|
|
* the original job may not have placed procs on every node. So
|
|
* we construct the map here by cycling across all nodes, adding
|
|
* only those nodes where num_procs > 0.
|
|
*/
|
|
for (i=0; i < orte_node_pool->size; i++) {
|
|
if (NULL == (node = (orte_node_t*)opal_pointer_array_get_item(orte_node_pool, i))) {
|
|
continue;
|
|
}
|
|
/* if this node wasn't included in the vm, ignore it */
|
|
if (NULL == node->daemon) {
|
|
continue;
|
|
}
|
|
/* if the node doesn't have any app procs on it, ignore it */
|
|
if (node->num_procs < 1) {
|
|
continue;
|
|
}
|
|
/* this node has at least one proc, so add it to our map */
|
|
OBJ_RETAIN(node);
|
|
opal_pointer_array_add(debugger->map->nodes, node);
|
|
debugger->map->num_nodes++;
|
|
/* add a debugger daemon to the node - note that the
|
|
* debugger daemon does NOT count against our subscribed slots
|
|
*/
|
|
proc = OBJ_NEW(orte_proc_t);
|
|
proc->name.jobid = debugger->jobid;
|
|
proc->name.vpid = vpid++;
|
|
/* point the proc at the local ORTE daemon as its parent */
|
|
proc->parent = node->daemon->name.vpid;
|
|
/* set the local/node ranks - we don't actually care
|
|
* what these are, but the odls needs them
|
|
*/
|
|
proc->local_rank = 0;
|
|
proc->node_rank = 0;
|
|
proc->app_rank = proc->name.vpid;
|
|
/* flag the proc as ready for launch */
|
|
proc->state = ORTE_PROC_STATE_INIT;
|
|
proc->app_idx = 0;
|
|
|
|
OBJ_RETAIN(node); /* maintain accounting on object */
|
|
proc->node = node;
|
|
/* add the proc to the job */
|
|
opal_pointer_array_set_item(debugger->procs, proc->name.vpid, proc);
|
|
debugger->num_procs++;
|
|
|
|
/* add the proc to the node's array */
|
|
OBJ_RETAIN(proc);
|
|
opal_pointer_array_add(node->procs, (void*)proc);
|
|
node->num_procs++;
|
|
}
|
|
/* schedule it for launch */
|
|
debugger->state = ORTE_JOB_STATE_INIT;
|
|
ORTE_ACTIVATE_JOB_STATE(debugger, ORTE_JOB_STATE_LAUNCH_APPS);
|
|
}
|
|
|
|
/*
|
|
* Initialization of data structures for running under a debugger
|
|
* using the MPICH/TotalView parallel debugger interface. This stage
|
|
* of initialization must occur after spawn
|
|
*
|
|
* NOTE: We -always- perform this step to ensure that any debugger
|
|
* that attaches to us post-launch of the application can get a
|
|
* completed proctable
|
|
*/
|
|
void orte_debugger_init_after_spawn(int fd, short event, void *cbdata)
|
|
{
|
|
orte_state_caddy_t *caddy = (orte_state_caddy_t*)cbdata;
|
|
orte_job_t *jdata = caddy->jdata;
|
|
orte_proc_t *proc;
|
|
orte_app_context_t *appctx;
|
|
orte_vpid_t i, j;
|
|
char **aliases, *aptr;
|
|
|
|
/* if we couldn't get thru the mapper stage, we might
|
|
* enter here with no procs. Avoid the "zero byte malloc"
|
|
* message by checking here
|
|
*/
|
|
if (MPIR_proctable || 0 == jdata->num_procs) {
|
|
|
|
/* already initialized */
|
|
opal_output_verbose(5, orte_debug_output,
|
|
"%s: debugger already initialized or zero procs",
|
|
ORTE_NAME_PRINT(ORTE_PROC_MY_NAME));
|
|
|
|
if (MPIR_being_debugged || NULL != orte_debugger_test_daemon ||
|
|
NULL != getenv("ORTE_TEST_DEBUGGER_ATTACH")) {
|
|
OBJ_RELEASE(caddy);
|
|
if (!mpir_breakpoint_fired) {
|
|
/* record that we have triggered the debugger */
|
|
mpir_breakpoint_fired = true;
|
|
|
|
/* trigger the debugger */
|
|
MPIR_Breakpoint();
|
|
|
|
opal_output_verbose(5, orte_debug_output,
|
|
"%s NOTIFYING DEBUGGER RELEASE",
|
|
ORTE_NAME_PRINT(ORTE_PROC_MY_NAME));
|
|
/* notify all procs that the debugger is ready */
|
|
_send_notification();
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* fill in the proc table for the application processes */
|
|
|
|
opal_output_verbose(5, orte_debug_output,
|
|
"%s: Setting up debugger process table for applications",
|
|
ORTE_NAME_PRINT(ORTE_PROC_MY_NAME));
|
|
|
|
MPIR_debug_state = 1;
|
|
|
|
/* set the total number of processes in the job */
|
|
MPIR_proctable_size = jdata->num_procs;
|
|
|
|
/* allocate MPIR_proctable */
|
|
MPIR_proctable = (struct MPIR_PROCDESC *)malloc(sizeof(struct MPIR_PROCDESC) *
|
|
MPIR_proctable_size);
|
|
if (MPIR_proctable == NULL) {
|
|
ORTE_ERROR_LOG(ORTE_ERR_OUT_OF_RESOURCE);
|
|
OBJ_RELEASE(caddy);
|
|
return;
|
|
}
|
|
|
|
if (orte_debugger_dump_proctable) {
|
|
opal_output(orte_clean_output, "MPIR Proctable for job %s", ORTE_JOBID_PRINT(jdata->jobid));
|
|
}
|
|
|
|
/* initialize MPIR_proctable */
|
|
for (j=0; j < jdata->num_procs; j++) {
|
|
if (NULL == (proc = (orte_proc_t*)opal_pointer_array_get_item(jdata->procs, j))) {
|
|
continue;
|
|
}
|
|
/* store this data in the location whose index
|
|
* corresponds to the proc's rank
|
|
*/
|
|
i = proc->name.vpid;
|
|
if (NULL == (appctx = (orte_app_context_t*)opal_pointer_array_get_item(jdata->apps, proc->app_idx))) {
|
|
continue;
|
|
}
|
|
|
|
/* take the indicated alias as the hostname, if aliases exist */
|
|
if (orte_retain_aliases) {
|
|
aliases = NULL;
|
|
aptr = NULL;
|
|
if (orte_get_attribute(&proc->node->attributes, ORTE_NODE_ALIAS, (void**)&aptr, OPAL_STRING)) {
|
|
aliases = opal_argv_split(aptr, ',');
|
|
free(aptr);
|
|
if (orte_use_hostname_alias <= opal_argv_count(aliases)) {
|
|
MPIR_proctable[i].host_name = strdup(aliases[orte_use_hostname_alias-1]);
|
|
}
|
|
opal_argv_free(aliases);
|
|
}
|
|
} else {
|
|
/* just use the default name */
|
|
MPIR_proctable[i].host_name = strdup(proc->node->name);
|
|
}
|
|
|
|
if ( 0 == strncmp(appctx->app, OPAL_PATH_SEP, 1 )) {
|
|
MPIR_proctable[i].executable_name =
|
|
opal_os_path( false, appctx->app, NULL );
|
|
} else {
|
|
MPIR_proctable[i].executable_name =
|
|
opal_os_path( false, appctx->cwd, appctx->app, NULL );
|
|
}
|
|
MPIR_proctable[i].pid = proc->pid;
|
|
if (orte_debugger_dump_proctable) {
|
|
opal_output(orte_clean_output, "%s: Host %s Exe %s Pid %d",
|
|
ORTE_VPID_PRINT(i), MPIR_proctable[i].host_name,
|
|
MPIR_proctable[i].executable_name, MPIR_proctable[i].pid);
|
|
}
|
|
}
|
|
|
|
if (0 < opal_output_get_verbosity(orte_debug_output)) {
|
|
orte_debugger_dump();
|
|
}
|
|
|
|
/* if we are being launched under a debugger, then we must wait
|
|
* for it to be ready to go and do some things to start the job
|
|
*/
|
|
if (MPIR_being_debugged || NULL != orte_debugger_test_daemon ||
|
|
NULL != getenv("ORTE_TEST_DEBUGGER_ATTACH")) {
|
|
/* if we are not launching debugger daemons, then trigger
|
|
* the debugger - otherwise, we need to wait for the debugger
|
|
* daemons to be started
|
|
*/
|
|
if ('\0' == MPIR_executable_path[0] && NULL == orte_debugger_test_daemon) {
|
|
/* record that we have triggered the debugger */
|
|
mpir_breakpoint_fired = true;
|
|
|
|
/* trigger the debugger */
|
|
MPIR_Breakpoint();
|
|
|
|
opal_output_verbose(2, orte_debug_output,
|
|
"%s NOTIFYING DEBUGGER RELEASE",
|
|
ORTE_NAME_PRINT(ORTE_PROC_MY_NAME));
|
|
/* notify all procs that the debugger is ready */
|
|
_send_notification();
|
|
} else if (!orte_debugger_test_attach) {
|
|
/* if I am launching debugger daemons, then I need to do so now
|
|
* that the job has been started and I know which nodes have
|
|
* apps on them
|
|
*/
|
|
opal_output_verbose(2, orte_debug_output,
|
|
"%s Cospawning debugger daemons %s",
|
|
ORTE_NAME_PRINT(ORTE_PROC_MY_NAME),
|
|
(NULL == orte_debugger_test_daemon) ?
|
|
MPIR_executable_path : orte_debugger_test_daemon);
|
|
setup_debugger_job();
|
|
}
|
|
/* we don't have anything else to do */
|
|
OBJ_RELEASE(caddy);
|
|
return;
|
|
}
|
|
|
|
/* if we are not being debugged, then just cleanup and depart */
|
|
OBJ_RELEASE(caddy);
|
|
}
|
|
|
|
/*
|
|
* Process one line from the orte_base_user_debugger MCA param and
|
|
* look for that debugger in the path. If we find it, fill in
|
|
* new_argv.
|
|
*/
|
|
static int process(char *orig_line, char *basename, opal_cmd_line_t *cmd_line,
|
|
int argc, char **argv, char ***new_argv, int num_procs)
|
|
{
|
|
int ret = ORTE_SUCCESS;
|
|
int i, j, count;
|
|
char *line = NULL, *tmp = NULL, *full_line = strdup(orig_line);
|
|
char **orterun_argv = NULL, **executable_argv = NULL, **line_argv = NULL;
|
|
char cwd[OPAL_PATH_MAX];
|
|
bool used_num_procs = false;
|
|
bool single_app = false;
|
|
bool fail_needed_executable = false;
|
|
|
|
line = full_line;
|
|
if (NULL == line) {
|
|
ret = ORTE_ERR_OUT_OF_RESOURCE;
|
|
goto out;
|
|
}
|
|
|
|
/* Trim off whitespace at the beginning and ending of line */
|
|
|
|
for (i = 0; '\0' != line[i] && isspace(line[i]); ++line) {
|
|
continue;
|
|
}
|
|
for (i = strlen(line) - 2; i > 0 && isspace(line[i]); ++i) {
|
|
line[i] = '\0';
|
|
}
|
|
if (strlen(line) <= 0) {
|
|
ret = ORTE_ERROR;
|
|
goto out;
|
|
}
|
|
|
|
/* Get the tail of the command line (i.e., the user executable /
|
|
argv) */
|
|
|
|
opal_cmd_line_get_tail(cmd_line, &i, &executable_argv);
|
|
|
|
/* Make a new copy of the orterun command line args, without the
|
|
orterun token itself, and without the --debug, --debugger, and
|
|
-tv flags. */
|
|
|
|
orterun_argv = opal_argv_copy(argv);
|
|
count = opal_argv_count(orterun_argv);
|
|
opal_argv_delete(&count, &orterun_argv, 0, 1);
|
|
for (i = 0; NULL != orterun_argv[i]; ++i) {
|
|
count = opal_argv_count(orterun_argv);
|
|
if (0 == strcmp(orterun_argv[i], "-debug") ||
|
|
0 == strcmp(orterun_argv[i], "--debug")) {
|
|
opal_argv_delete(&count, &orterun_argv, i, 1);
|
|
} else if (0 == strcmp(orterun_argv[i], "-tv") ||
|
|
0 == strcmp(orterun_argv[i], "--tv")) {
|
|
opal_argv_delete(&count, &orterun_argv, i, 1);
|
|
} else if (0 == strcmp(orterun_argv[i], "--debugger") ||
|
|
0 == strcmp(orterun_argv[i], "-debugger")) {
|
|
opal_argv_delete(&count, &orterun_argv, i, 2);
|
|
}
|
|
}
|
|
|
|
/* Replace @@ tokens - line should never realistically be bigger
|
|
than MAX_INT, so just cast to int to remove compiler warning */
|
|
|
|
*new_argv = NULL;
|
|
line_argv = opal_argv_split(line, ' ');
|
|
if (NULL == line_argv) {
|
|
ret = ORTE_ERR_NOT_FOUND;
|
|
goto out;
|
|
}
|
|
for (i = 0; NULL != line_argv[i]; ++i) {
|
|
if (0 == strcmp(line_argv[i], "@mpirun@") ||
|
|
0 == strcmp(line_argv[i], "@orterun@")) {
|
|
opal_argv_append_nosize(new_argv, argv[0]);
|
|
} else if (0 == strcmp(line_argv[i], "@mpirun_args@") ||
|
|
0 == strcmp(line_argv[i], "@orterun_args@")) {
|
|
for (j = 0; NULL != orterun_argv && NULL != orterun_argv[j]; ++j) {
|
|
opal_argv_append_nosize(new_argv, orterun_argv[j]);
|
|
}
|
|
} else if (0 == strcmp(line_argv[i], "@np@")) {
|
|
used_num_procs = true;
|
|
asprintf(&tmp, "%d", num_procs);
|
|
opal_argv_append_nosize(new_argv, tmp);
|
|
free(tmp);
|
|
} else if (0 == strcmp(line_argv[i], "@single_app@")) {
|
|
/* This token is only a flag; it is not replaced with any
|
|
alternate text */
|
|
single_app = true;
|
|
} else if (0 == strcmp(line_argv[i], "@executable@")) {
|
|
/* If we found the executable, paste it in. Otherwise,
|
|
this is a possible error. */
|
|
if (NULL != executable_argv) {
|
|
opal_argv_append_nosize(new_argv, executable_argv[0]);
|
|
} else {
|
|
fail_needed_executable = true;
|
|
}
|
|
} else if (0 == strcmp(line_argv[i], "@executable_argv@")) {
|
|
/* If we found the tail, paste in the argv. Otherwise,
|
|
this is a possible error. */
|
|
if (NULL != executable_argv) {
|
|
for (j = 1; NULL != executable_argv[j]; ++j) {
|
|
opal_argv_append_nosize(new_argv, executable_argv[j]);
|
|
}
|
|
} else {
|
|
fail_needed_executable = true;
|
|
}
|
|
} else {
|
|
/* It wasn't a special token, so just copy it over */
|
|
opal_argv_append_nosize(new_argv, line_argv[i]);
|
|
}
|
|
}
|
|
|
|
/* Can we find argv[0] in the path? */
|
|
|
|
getcwd(cwd, OPAL_PATH_MAX);
|
|
tmp = opal_path_findv((*new_argv)[0], X_OK, environ, cwd);
|
|
if (NULL != tmp) {
|
|
free(tmp);
|
|
|
|
/* Ok, we found a good debugger. Check for some error
|
|
conditions. */
|
|
tmp = opal_argv_join(argv, ' ');
|
|
|
|
/* We do not support launching a debugger that requires the
|
|
-np value if the user did not specify -np on the command
|
|
line. */
|
|
if (used_num_procs && 0 == num_procs) {
|
|
free(tmp);
|
|
tmp = opal_argv_join(orterun_argv, ' ');
|
|
orte_show_help("help-orterun.txt", "debugger requires -np",
|
|
true, (*new_argv)[0], argv[0], tmp,
|
|
(*new_argv)[0]);
|
|
/* Fall through to free / fail, below */
|
|
}
|
|
|
|
/* Some debuggers do not support launching MPMD */
|
|
else if (single_app && NULL != strstr(tmp, " : ")) {
|
|
orte_show_help("help-orterun.txt",
|
|
"debugger only accepts single app", true,
|
|
(*new_argv)[0], (*new_argv)[0]);
|
|
/* Fall through to free / fail, below */
|
|
}
|
|
|
|
/* Some debuggers do not use orterun/mpirun, and therefore
|
|
must have an executable to run (e.g., cannot use mpirun's
|
|
app context file feature). */
|
|
else if (fail_needed_executable) {
|
|
orte_show_help("help-orterun.txt",
|
|
"debugger requires executable", true,
|
|
(*new_argv)[0], argv[0], (*new_argv)[0], argv[0],
|
|
(*new_argv)[0]);
|
|
/* Fall through to free / fail, below */
|
|
}
|
|
|
|
/* Otherwise, we succeeded. Return happiness. */
|
|
else {
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/* All done -- didn't find it */
|
|
|
|
opal_argv_free(*new_argv);
|
|
*new_argv = NULL;
|
|
ret = ORTE_ERR_NOT_FOUND;
|
|
|
|
out:
|
|
if (NULL != orterun_argv) {
|
|
opal_argv_free(orterun_argv);
|
|
}
|
|
if (NULL != executable_argv) {
|
|
opal_argv_free(executable_argv);
|
|
}
|
|
if (NULL != line_argv) {
|
|
opal_argv_free(line_argv);
|
|
}
|
|
if (NULL != tmp) {
|
|
free(tmp);
|
|
}
|
|
if (NULL != full_line) {
|
|
free(full_line);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void open_fifo(void)
|
|
{
|
|
if (orte_debugger_attach_fd > 0) {
|
|
close(orte_debugger_attach_fd);
|
|
}
|
|
|
|
orte_debugger_attach_fd = open(MPIR_attach_fifo, O_RDONLY | O_NONBLOCK, 0);
|
|
if (orte_debugger_attach_fd < 0) {
|
|
opal_output(0, "%s unable to open debugger attach fifo",
|
|
ORTE_NAME_PRINT(ORTE_PROC_MY_NAME));
|
|
return;
|
|
}
|
|
|
|
/* Set this fd to be close-on-exec so that children don't see it */
|
|
if (opal_fd_set_cloexec(orte_debugger_attach_fd) != OPAL_SUCCESS) {
|
|
opal_output(0, "%s unable to set debugger attach fifo to CLOEXEC",
|
|
ORTE_NAME_PRINT(ORTE_PROC_MY_NAME));
|
|
close(orte_debugger_attach_fd);
|
|
orte_debugger_attach_fd = -1;
|
|
return;
|
|
}
|
|
|
|
if (orte_debugger_test_attach) {
|
|
opal_output(0, "%s Monitoring debugger attach fifo %s",
|
|
ORTE_NAME_PRINT(ORTE_PROC_MY_NAME),
|
|
MPIR_attach_fifo);
|
|
} else {
|
|
opal_output_verbose(2, orte_debug_output,
|
|
"%s Monitoring debugger attach fifo %s",
|
|
ORTE_NAME_PRINT(ORTE_PROC_MY_NAME),
|
|
MPIR_attach_fifo);
|
|
}
|
|
orte_debugger_attach = (opal_event_t*)malloc(sizeof(opal_event_t));
|
|
opal_event_set(orte_event_base, orte_debugger_attach, orte_debugger_attach_fd,
|
|
OPAL_EV_READ, attach_debugger, orte_debugger_attach);
|
|
|
|
orte_debugger_fifo_active = true;
|
|
opal_event_add(orte_debugger_attach, 0);
|
|
}
|
|
|
|
static bool did_once = false;
|
|
|
|
static void attach_debugger(int fd, short event, void *arg)
|
|
{
|
|
unsigned char fifo_cmd;
|
|
int rc;
|
|
orte_timer_t *tm;
|
|
|
|
if (orte_debugger_fifo_active) {
|
|
orte_debugger_attach = (opal_event_t*)arg;
|
|
orte_debugger_fifo_active = false;
|
|
|
|
rc = read(orte_debugger_attach_fd, &fifo_cmd, sizeof(fifo_cmd));
|
|
if (!rc) {
|
|
/* release the current event */
|
|
opal_event_free(orte_debugger_attach);
|
|
/* reopen device to clear hangup */
|
|
open_fifo();
|
|
return;
|
|
}
|
|
if (1 != fifo_cmd) {
|
|
/* ignore the cmd */
|
|
orte_debugger_fifo_active = true;
|
|
opal_event_add(orte_debugger_attach, 0);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!MPIR_being_debugged && !orte_debugger_test_attach) {
|
|
/* false alarm - reset the read or timer event */
|
|
if (0 == orte_debugger_check_rate) {
|
|
orte_debugger_fifo_active = true;
|
|
opal_event_add(orte_debugger_attach, 0);
|
|
} else if (!MPIR_being_debugged) {
|
|
tm = (orte_timer_t*)arg;
|
|
/* re-add the event */
|
|
opal_event_evtimer_add(tm->ev, &tm->tv);
|
|
}
|
|
return;
|
|
}
|
|
|
|
opal_output_verbose(1, orte_debug_output,
|
|
"%s Attaching debugger %s", ORTE_NAME_PRINT(ORTE_PROC_MY_NAME),
|
|
(NULL == orte_debugger_test_daemon) ? MPIR_executable_path : orte_debugger_test_daemon);
|
|
|
|
/* a debugger has attached! All the MPIR_Proctable
|
|
* data is already available, so we only need to
|
|
* check to see if we should spawn any daemons
|
|
*/
|
|
if ('\0' != MPIR_executable_path[0] || NULL != orte_debugger_test_daemon) {
|
|
opal_output_verbose(2, orte_debug_output,
|
|
"%s Spawning debugger daemons %s",
|
|
ORTE_NAME_PRINT(ORTE_PROC_MY_NAME),
|
|
(NULL == orte_debugger_test_daemon) ?
|
|
MPIR_executable_path : orte_debugger_test_daemon);
|
|
setup_debugger_job();
|
|
did_once = true;
|
|
}
|
|
|
|
/* if we are testing, ensure we only do this once */
|
|
if (NULL != orte_debugger_test_daemon && did_once) {
|
|
return;
|
|
}
|
|
|
|
/* reset the read or timer event */
|
|
if (0 == orte_debugger_check_rate) {
|
|
orte_debugger_fifo_active = true;
|
|
opal_event_add(orte_debugger_attach, 0);
|
|
} else if (!MPIR_being_debugged) {
|
|
tm = (orte_timer_t*)arg;
|
|
/* re-add the event */
|
|
opal_event_evtimer_add(tm->ev, &tm->tv);
|
|
}
|
|
}
|
|
|
|
static void build_debugger_args(orte_app_context_t *debugger)
|
|
{
|
|
int i, j;
|
|
char mpir_arg[MPIR_MAX_ARG_LENGTH];
|
|
|
|
if ('\0' != MPIR_server_arguments[0]) {
|
|
j=0;
|
|
memset(mpir_arg, 0, MPIR_MAX_ARG_LENGTH);
|
|
for (i=0; i < MPIR_MAX_ARG_LENGTH; i++) {
|
|
if (MPIR_server_arguments[i] == '\0') {
|
|
if (0 < j) {
|
|
opal_argv_append_nosize(&debugger->argv, mpir_arg);
|
|
memset(mpir_arg, 0, MPIR_MAX_ARG_LENGTH);
|
|
j=0;
|
|
}
|
|
} else {
|
|
mpir_arg[j] = MPIR_server_arguments[i];
|
|
j++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Run a user-level debugger
|
|
*/
|
|
static void run_debugger(char *basename, opal_cmd_line_t *cmd_line,
|
|
int argc, char *argv[], int num_procs)
|
|
{
|
|
int i, id, ret;
|
|
char **new_argv = NULL;
|
|
const char **tmp = NULL;
|
|
char *value, **lines, *env_name;
|
|
|
|
/* Get the orte_base_debug MCA parameter and search for a debugger
|
|
that can run */
|
|
|
|
id = mca_base_var_find("orte", "orte", NULL, "base_user_debugger");
|
|
if (id < 0) {
|
|
orte_show_help("help-orterun.txt", "debugger-mca-param-not-found",
|
|
true);
|
|
exit(1);
|
|
}
|
|
|
|
ret = mca_base_var_get_value (id, &tmp, NULL, NULL);
|
|
if (OPAL_SUCCESS != ret || NULL == tmp || NULL == tmp[0]) {
|
|
orte_show_help("help-orterun.txt", "debugger-orte_base_user_debugger-empty",
|
|
true);
|
|
exit(1);
|
|
}
|
|
|
|
/* Look through all the values in the MCA param */
|
|
|
|
lines = opal_argv_split(tmp[0], ':');
|
|
for (i = 0; NULL != lines[i]; ++i) {
|
|
if (ORTE_SUCCESS == process(lines[i], basename, cmd_line, argc, argv,
|
|
&new_argv, num_procs)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* If we didn't find one, abort */
|
|
|
|
if (NULL == lines[i]) {
|
|
orte_show_help("help-orterun.txt", "debugger-not-found", true);
|
|
exit(1);
|
|
}
|
|
opal_argv_free(lines);
|
|
|
|
/* We found one */
|
|
|
|
/* cleanup the MPIR arrays in case the debugger doesn't set them */
|
|
memset((char*)MPIR_executable_path, 0, MPIR_MAX_PATH_LENGTH);
|
|
memset((char*)MPIR_server_arguments, 0, MPIR_MAX_ARG_LENGTH);
|
|
|
|
/* Set an MCA param so that everyone knows that they are being
|
|
launched under a debugger; not all debuggers are consistent
|
|
about setting MPIR_being_debugged in both the launcher and the
|
|
MPI processes */
|
|
ret = mca_base_var_env_name ("orte_in_parallel_debugger", &env_name);
|
|
if (OPAL_SUCCESS == ret && NULL != env_name) {
|
|
opal_setenv(env_name, "1", true, &environ);
|
|
free(env_name);
|
|
}
|
|
|
|
/* Launch the debugger */
|
|
execvp(new_argv[0], new_argv);
|
|
value = opal_argv_join(new_argv, ' ');
|
|
orte_show_help("help-orterun.txt", "debugger-exec-failed",
|
|
true, basename, value, new_argv[0]);
|
|
free(value);
|
|
opal_argv_free(new_argv);
|
|
exit(1);
|
|
}
|
|
|
|
void orte_debugger_detached(int fd, short event, void *cbdata)
|
|
{
|
|
orte_state_caddy_t *caddy = (orte_state_caddy_t*)cbdata;
|
|
OBJ_RELEASE(caddy);
|
|
|
|
/* need to ensure MPIR_Breakpoint is called again if another debugger attaches */
|
|
mpir_breakpoint_fired = false;
|
|
}
|
|
|
|
static uint32_t ntraces = 0;
|
|
static orte_timer_t stack_trace_timer;
|
|
|
|
static void stack_trace_recv(int status, orte_process_name_t* sender,
|
|
opal_buffer_t *buffer, orte_rml_tag_t tag,
|
|
void* cbdata)
|
|
{
|
|
opal_buffer_t *blob;
|
|
char *st;
|
|
int32_t cnt;
|
|
orte_process_name_t name;
|
|
char *hostname;
|
|
pid_t pid;
|
|
|
|
/* unpack the stack_trace blob */
|
|
cnt = 1;
|
|
while (OPAL_SUCCESS == opal_dss.unpack(buffer, &blob, &cnt, OPAL_BUFFER)) {
|
|
/* first piece is the name of the process */
|
|
cnt = 1;
|
|
if (OPAL_SUCCESS != opal_dss.unpack(blob, &name, &cnt, ORTE_NAME) ||
|
|
OPAL_SUCCESS != opal_dss.unpack(blob, &hostname, &cnt, OPAL_STRING) ||
|
|
OPAL_SUCCESS != opal_dss.unpack(blob, &pid, &cnt, OPAL_PID)) {
|
|
OBJ_RELEASE(blob);
|
|
continue;
|
|
}
|
|
fprintf(stderr, "STACK TRACE FOR PROC %s (%s, PID %lu)\n", ORTE_NAME_PRINT(&name), hostname, (unsigned long) pid);
|
|
free(hostname);
|
|
/* unpack the stack_trace until complete */
|
|
cnt = 1;
|
|
while (OPAL_SUCCESS == opal_dss.unpack(blob, &st, &cnt, OPAL_STRING)) {
|
|
fprintf(stderr, "\t%s", st); // has its own newline
|
|
free(st);
|
|
cnt = 1;
|
|
}
|
|
fprintf(stderr, "\n");
|
|
OBJ_RELEASE(blob);
|
|
cnt = 1;
|
|
}
|
|
++ntraces;
|
|
if (orte_process_info.num_procs == ntraces) {
|
|
/* cancel the timeout */
|
|
OBJ_DESTRUCT(&stack_trace_timer);
|
|
/* abort the job */
|
|
ORTE_ACTIVATE_JOB_STATE(NULL, ORTE_JOB_STATE_ALL_JOBS_COMPLETE);
|
|
/* set the global abnormal exit flag */
|
|
orte_abnormal_term_ordered = true;
|
|
}
|
|
}
|
|
|
|
static void stack_trace_timeout(int sd, short args, void *cbdata)
|
|
{
|
|
/* abort the job */
|
|
ORTE_ACTIVATE_JOB_STATE(NULL, ORTE_JOB_STATE_ALL_JOBS_COMPLETE);
|
|
/* set the global abnormal exit flag */
|
|
orte_abnormal_term_ordered = true;
|
|
}
|
|
|
|
void orte_timeout_wakeup(int sd, short args, void *cbdata)
|
|
{
|
|
orte_job_t *jdata;
|
|
orte_proc_t *proc;
|
|
int i;
|
|
int rc;
|
|
uint32_t key;
|
|
void *nptr;
|
|
|
|
/* this function gets called when the job execution time
|
|
* has hit a prescribed limit - so just abort
|
|
*/
|
|
orte_show_help("help-orterun.txt", "orterun:timeout",
|
|
true, timeout_seconds);
|
|
ORTE_UPDATE_EXIT_STATUS(ETIMEDOUT);
|
|
/* if we are testing HNP suicide, then just exit */
|
|
if (ORTE_PROC_IS_HNP &&
|
|
NULL != getenv("ORTE_TEST_HNP_SUICIDE")) {
|
|
opal_output(0, "HNP exiting w/o cleanup");
|
|
exit(1);
|
|
}
|
|
if (orte_cmd_options.report_state_on_timeout) {
|
|
/* cycle across all the jobs and report their state */
|
|
rc = opal_hash_table_get_first_key_uint32(orte_job_data, &key, (void **)&jdata, &nptr);
|
|
while (OPAL_SUCCESS == rc) {
|
|
/* don't use the opal_output system as it may be borked */
|
|
fprintf(stderr, "DATA FOR JOB: %s\n", ORTE_JOBID_PRINT(jdata->jobid));
|
|
fprintf(stderr, "\tNum apps: %d\tNum procs: %d\tJobState: %s\tAbort: %s\n",
|
|
(int)jdata->num_apps, (int)jdata->num_procs,
|
|
orte_job_state_to_str(jdata->state),
|
|
(ORTE_FLAG_TEST(jdata, ORTE_JOB_FLAG_ABORTED)) ? "True" : "False");
|
|
fprintf(stderr, "\tNum launched: %ld\tNum reported: %ld\tNum terminated: %ld\n",
|
|
(long)jdata->num_launched, (long)jdata->num_reported, (long)jdata->num_terminated);
|
|
fprintf(stderr, "\n\tProcs:\n");
|
|
for (i=0; i < jdata->procs->size; i++) {
|
|
if (NULL != (proc = (orte_proc_t*)opal_pointer_array_get_item(jdata->procs, i))) {
|
|
fprintf(stderr, "\t\tRank: %s\tNode: %s\tPID: %u\tState: %s\tExitCode %d\n",
|
|
ORTE_VPID_PRINT(proc->name.vpid),
|
|
(NULL == proc->node) ? "UNKNOWN" : proc->node->name,
|
|
(unsigned int)proc->pid,
|
|
orte_proc_state_to_str(proc->state), proc->exit_code);
|
|
}
|
|
}
|
|
fprintf(stderr, "\n");
|
|
rc = opal_hash_table_get_next_key_uint32(orte_job_data, &key, (void **)&jdata, nptr, &nptr);
|
|
}
|
|
}
|
|
/* if they asked for stack_traces, attempt to get them, but timeout
|
|
* if we cannot do so */
|
|
if (orte_cmd_options.get_stack_traces) {
|
|
orte_daemon_cmd_flag_t command = ORTE_DAEMON_GET_STACK_TRACES;
|
|
opal_buffer_t *buffer;
|
|
orte_grpcomm_signature_t *sig;
|
|
|
|
fprintf(stderr, "Waiting for stack traces (this may take a few moments)...\n");
|
|
|
|
/* set the recv */
|
|
orte_rml.recv_buffer_nb(ORTE_NAME_WILDCARD, ORTE_RML_TAG_STACK_TRACE,
|
|
ORTE_RML_PERSISTENT, stack_trace_recv, NULL);
|
|
|
|
/* setup the buffer */
|
|
buffer = OBJ_NEW(opal_buffer_t);
|
|
/* pack the command */
|
|
if (ORTE_SUCCESS != (rc = opal_dss.pack(buffer, &command, 1, ORTE_DAEMON_CMD))) {
|
|
ORTE_ERROR_LOG(rc);
|
|
OBJ_RELEASE(buffer);
|
|
goto giveup;
|
|
}
|
|
/* goes to all daemons */
|
|
sig = OBJ_NEW(orte_grpcomm_signature_t);
|
|
sig->signature = (orte_process_name_t*)malloc(sizeof(orte_process_name_t));
|
|
sig->signature[0].jobid = ORTE_PROC_MY_NAME->jobid;
|
|
sig->signature[0].vpid = ORTE_VPID_WILDCARD;
|
|
sig->sz = 1;
|
|
if (ORTE_SUCCESS != (rc = orte_grpcomm.xcast(sig, ORTE_RML_TAG_DAEMON, buffer))) {
|
|
ORTE_ERROR_LOG(rc);
|
|
OBJ_RELEASE(buffer);
|
|
OBJ_RELEASE(sig);
|
|
goto giveup;
|
|
}
|
|
OBJ_RELEASE(buffer);
|
|
/* maintain accounting */
|
|
OBJ_RELEASE(sig);
|
|
/* we will terminate after we get the stack_traces, but set a timeout
|
|
* just in case we never hear back from everyone */
|
|
OBJ_CONSTRUCT(&stack_trace_timer, orte_timer_t);
|
|
opal_event_evtimer_set(orte_event_base,
|
|
stack_trace_timer.ev, stack_trace_timeout, NULL);
|
|
opal_event_set_priority(stack_trace_timer.ev, ORTE_ERROR_PRI);
|
|
stack_trace_timer.tv.tv_sec = 30;
|
|
opal_event_evtimer_add(stack_trace_timer.ev, &stack_trace_timer.tv);
|
|
return;
|
|
}
|
|
giveup:
|
|
/* abort the job */
|
|
ORTE_ACTIVATE_JOB_STATE(NULL, ORTE_JOB_STATE_ALL_JOBS_COMPLETE);
|
|
/* set the global abnormal exit flag */
|
|
orte_abnormal_term_ordered = true;
|
|
}
|
|
|
|
static int nreports = 0;
|
|
static orte_timer_t profile_timer;
|
|
|
|
static void profile_recv(int status, orte_process_name_t* sender,
|
|
opal_buffer_t *buffer, orte_rml_tag_t tag,
|
|
void* cbdata)
|
|
{
|
|
int32_t cnt;
|
|
char *hostname;
|
|
float dpss, pss;
|
|
|
|
/* unpack the hostname where this came from */
|
|
cnt = 1;
|
|
if (OPAL_SUCCESS != opal_dss.unpack(buffer, &hostname, &cnt, OPAL_STRING)) {
|
|
goto done;
|
|
}
|
|
/* print the hostname */
|
|
fprintf(stderr, "Memory profile from host: %s\n", hostname);
|
|
free(hostname);
|
|
|
|
/* get the PSS of the daemon */
|
|
cnt = 1;
|
|
if (OPAL_SUCCESS != opal_dss.unpack(buffer, &dpss, &cnt, OPAL_FLOAT)) {
|
|
goto done;
|
|
}
|
|
/* get the average PSS of the child procs */
|
|
cnt = 1;
|
|
if (OPAL_SUCCESS != opal_dss.unpack(buffer, &pss, &cnt, OPAL_FLOAT)) {
|
|
goto done;
|
|
}
|
|
|
|
fprintf(stderr, "\tDaemon: %8.2fM\tProcs: %8.2fM\n", dpss, pss);
|
|
|
|
done:
|
|
--nreports;
|
|
if (nreports == 0) {
|
|
/* cancel the timeout */
|
|
OBJ_DESTRUCT(&profile_timer);
|
|
/* abort the job */
|
|
ORTE_ACTIVATE_JOB_STATE(NULL, ORTE_JOB_STATE_ALL_JOBS_COMPLETE);
|
|
/* set the global abnormal exit flag */
|
|
orte_abnormal_term_ordered = true;
|
|
}
|
|
}
|
|
|
|
static void profile_timeout(int sd, short args, void *cbdata)
|
|
{
|
|
/* abort the job */
|
|
ORTE_ACTIVATE_JOB_STATE(NULL, ORTE_JOB_STATE_ALL_JOBS_COMPLETE);
|
|
/* set the global abnormal exit flag */
|
|
orte_abnormal_term_ordered = true;
|
|
}
|
|
|
|
void orte_profile_wakeup(int sd, short args, void *cbdata)
|
|
{
|
|
orte_job_t *jdata = (orte_job_t*)cbdata;
|
|
orte_job_t *dmns;
|
|
orte_proc_t *dmn;
|
|
int i;
|
|
int rc;
|
|
orte_daemon_cmd_flag_t command = ORTE_DAEMON_GET_MEMPROFILE;
|
|
opal_buffer_t *buffer;
|
|
orte_process_name_t name;
|
|
|
|
/* this function gets called when the job execution time
|
|
* has hit a specified limit - collect profile data and
|
|
* abort this job, but timeout if we cannot do so
|
|
*/
|
|
|
|
/* set the recv */
|
|
nreports = 1; // always get a report from ourselves
|
|
orte_rml.recv_buffer_nb(ORTE_NAME_WILDCARD, ORTE_RML_TAG_MEMPROFILE,
|
|
ORTE_RML_PERSISTENT, profile_recv, NULL);
|
|
|
|
/* setup the buffer */
|
|
buffer = OBJ_NEW(opal_buffer_t);
|
|
/* pack the command */
|
|
if (ORTE_SUCCESS != (rc = opal_dss.pack(buffer, &command, 1, ORTE_DAEMON_CMD))) {
|
|
ORTE_ERROR_LOG(rc);
|
|
OBJ_RELEASE(buffer);
|
|
goto giveup;
|
|
}
|
|
/* pack the jobid in question */
|
|
if (ORTE_SUCCESS != (rc = opal_dss.pack(buffer, &jdata->jobid, 1, ORTE_JOBID))) {
|
|
ORTE_ERROR_LOG(rc);
|
|
OBJ_RELEASE(buffer);
|
|
goto giveup;
|
|
}
|
|
|
|
/* goes to just the first daemon beyond ourselves - no need to get it from everyone */
|
|
dmns = orte_get_job_data_object(ORTE_PROC_MY_NAME->jobid);
|
|
if (NULL != (dmn = (orte_proc_t*)opal_pointer_array_get_item(dmns->procs, 1))) {
|
|
++nreports;
|
|
}
|
|
|
|
/* send it out */
|
|
name.jobid = ORTE_PROC_MY_NAME->jobid;
|
|
for (i=0; i < nreports; i++) {
|
|
OBJ_RETAIN(buffer);
|
|
name.vpid = i;
|
|
if (0 > (rc = orte_rml.send_buffer_nb(orte_mgmt_conduit,
|
|
&name, buffer,
|
|
ORTE_RML_TAG_DAEMON,
|
|
orte_rml_send_callback, NULL))) {
|
|
ORTE_ERROR_LOG(rc);
|
|
OBJ_RELEASE(buffer);
|
|
}
|
|
}
|
|
OBJ_RELEASE(buffer); // maintain accounting
|
|
|
|
/* we will terminate after we get the profile, but set a timeout
|
|
* just in case we never hear back */
|
|
OBJ_CONSTRUCT(&profile_timer, orte_timer_t);
|
|
opal_event_evtimer_set(orte_event_base,
|
|
profile_timer.ev, profile_timeout, NULL);
|
|
opal_event_set_priority(profile_timer.ev, ORTE_ERROR_PRI);
|
|
profile_timer.tv.tv_sec = 30;
|
|
opal_event_evtimer_add(profile_timer.ev, &profile_timer.tv);
|
|
return;
|
|
|
|
giveup:
|
|
/* abort the job */
|
|
ORTE_ACTIVATE_JOB_STATE(NULL, ORTE_JOB_STATE_ALL_JOBS_COMPLETE);
|
|
}
|