/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil ; -*- */ /* * (C) 2003 by Argonne National Laboratory. * See COPYRIGHT in top-level directory. */ /* * This file defines routines that can be called by the pre and postamble * functions in MPIE_ForkProcesses to add output line labeling to stdout * and to stderr. * * These functions are very simple and assume that either stdout/err won't * block, or that causing mpiexec to block until stdout/err unblock is * acceptable. A more sophisticated approach would queue up the output * and take advantage of the IOLoop routine (in ioloop.c) to control * output. */ /* Allow printf in label output */ /* style: allow:printf:1 sig:0 */ #include "mpichconf.h" #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include "pmutil.h" #include "ioloop.h" #include "process.h" #include "labelout.h" #ifdef HAVE_SNPRINTF #define MPL_snprintf snprintf #ifdef NEEDS_SNPRINTF_DECL /* style: allow:sprintf:1 sig:0 */ int snprintf(char *, size_t, const char *, ...); #endif #endif #define MAX_LABEL 32 typedef struct { char label[MAX_LABEL]; int lastNL; FILE *dest; } IOLabel; static int IOLabelWriteLine(int, int, void *); static int IOLabelSetLabelText(const char[], char[], int, int, int); /* labels for stdout and stderr, initialized to the default values */ static char outLabelPattern[MAX_LABEL] = "%W@[%w]@%d>"; static char errLabelPattern[MAX_LABEL] = "%W@[%w]@%d(err)>"; /* By default, labels are turned off */ static int useLabels = 0; void IOLabelSetDefault(int flag) { useLabels = 1; } /* Redirect stdout and stderr to a handler */ int IOLabelSetupFDs(IOLabelSetup * iofds) { /* Each pipe call creates 2 fds, 0 for reading, 1 for writing */ if (pipe(iofds->readOut)) return -1; if (pipe(iofds->readErr)) return -1; return 0; } /* Close one side of each pipe pair and replace stdout/err with the pipes */ int IOLabelSetupInClient(IOLabelSetup * iofds) { close(iofds->readOut[0]); close(1); dup2(iofds->readOut[1], 1); close(iofds->readErr[0]); close(2); dup2(iofds->readErr[1], 2); return 0; } /* Close one side of the pipe pair and register a handler for the I/O */ int IOLabelSetupFinishInServer(IOLabelSetup * iofds, ProcessState * pState) { IOLabel *leader, *leadererr; close(iofds->readOut[1]); close(iofds->readErr[1]); /* We need dedicated storage for the private data */ leader = (IOLabel *) MPL_malloc(sizeof(IOLabel), MPL_MEM_PM); if (useLabels) { IOLabelSetLabelText(outLabelPattern, leader->label, sizeof(leader->label), pState->wRank, pState->app->pWorld->worldNum); } else { leader->label[0] = 0; } leader->lastNL = 1; leader->dest = stdout; leadererr = (IOLabel *) MPL_malloc(sizeof(IOLabel), MPL_MEM_PM); if (useLabels) { IOLabelSetLabelText(errLabelPattern, leadererr->label, sizeof(leadererr->label), pState->wRank, pState->app->pWorld->worldNum); } else { leadererr->label[0] = 0; } leadererr->lastNL = 1; leadererr->dest = stderr; MPIE_IORegister(iofds->readOut[0], IO_READ, IOLabelWriteLine, leader); MPIE_IORegister(iofds->readErr[0], IO_READ, IOLabelWriteLine, leadererr); /* subsequent forks should not get these fds */ fcntl(iofds->readOut[0], F_SETFD, FD_CLOEXEC); fcntl(iofds->readErr[0], F_SETFD, FD_CLOEXEC); return 0; } static int IOLabelWriteLine(int fd, int rdwr, void *data) { IOLabel *label = (IOLabel *) data; char buf[1024], *p; int n; MPIE_SYSCALL(n, read, (fd, buf, 1024)); if (n == 0) { /* If read blocks, then returning a 0 is end-of-file */ return 1; /* ? EOF */ } p = buf; while (n > 0) { int c; if (label->lastNL) { if (label->label[0]) { fprintf(label->dest, "%s", label->label); } label->lastNL = 0; } c = *p++; n--; if (fputc(c, label->dest) != c) return 1; label->lastNL = (c == '\n'); } return 0; } /* Get the prefix to use on output lines from the environment. This is a common routine so that the same environment variables and effects can be used by many versions of mpiexec The choices are these: MPIEXEC_PREFIX_DEFAULT set: stdout gets "rank>" stderr gets "rank(err)>" MPIEXEX_PREFIX_STDOUT and MPIEXEC_PREFIX_STDERR replace those choices. The value can contain %d for the rank and eventually %w for the number of MPI_COMM_WORLD (e.g., 0 for the original one and > 0 for any subsequent spawned processes. The syntax %W@...@ means: use @...@ as the syntax if world num is > 0, use NULL (i.e., no characters) if world num is 0. Thus, %W@[%w]:@%d> for process 1 in the original MPI_COMM_WORLD gives 1> and for process 3 in a the second spawned process gives [2]:3> (%W takes the following char and uses that character to deliniate the %W string. E.g., %W'...' and %W@...@ have the same effect, as long as the string between the deliminters contains neither @ nor '. */ /* Internal routine used to set the label text */ static int IOLabelSetLabelText(const char pattern[], char label[], int maxlabel, int rank, int worldnum) { const char *pin = pattern; char *pout = label; int dlen; int lenleft = maxlabel - 1; char rankAsChar[12]; char worldnumAsChar[12]; /* Convert the rank in world to characters */ MPL_snprintf(rankAsChar, sizeof(rankAsChar), "%d", rank); /* Convert the world number to characters */ MPL_snprintf(worldnumAsChar, sizeof(worldnumAsChar), "%d", worldnum); pout[0] = 0; /* Copy the pattern looking for format commands */ while (lenleft > 0 && *pin) { if (*pin == '%') { pin++; /* Get the control */ switch (*pin) { case '%': *pout++ = '%'; lenleft--; break; case 'd': dlen = (int) strlen(rankAsChar); if (dlen < lenleft) { MPL_strncpy(pout, rankAsChar, lenleft); pout += dlen; lenleft -= dlen; } else { *pout++ = '*'; lenleft--; } break; case 'w': dlen = (int) strlen(worldnumAsChar); if (dlen < lenleft) { MPL_strncpy(pout, worldnumAsChar, lenleft); pout += dlen; lenleft -= dlen; } else { *pout++ = '*'; lenleft--; } break; case 'W': { char delim = *++pin; char wPattern[MAX_LABEL], wLabel[MAX_LABEL]; char *wptr = wPattern; pin++; while (*pin && *pin != delim && (wptr - wPattern) < MAX_LABEL) { *wptr++ = *pin++; } *wptr = 0; if (worldnum > 0) { /* Recursively invoke the label routine */ IOLabelSetLabelText(wPattern, wLabel, sizeof(wLabel), rank, worldnum); dlen = (int) strlen(wLabel); if (dlen < lenleft) { MPL_strncpy(pout, wLabel, lenleft); pout += dlen; lenleft -= dlen; } else { *pout++ = '*'; lenleft--; } } } break; default: /* Ignore the control */ *pout++ = '%'; lenleft--; if (lenleft--) *pout++ = *pin; } pin++; } else { *pout++ = *pin++; lenleft--; } } *pout = 0; return 0; } /* Check the environment for label control If either prefix is set, the labels are enabled. If only one prefix is set, the default prefix is used for the other label */ int IOLabelCheckEnv(void) { char *envval; envval = getenv("MPIEXEC_PREFIX_STDOUT"); if (envval) { if (strlen(envval) < MAX_LABEL) { MPL_strncpy(outLabelPattern, envval, MAX_LABEL); useLabels = 1; } else { MPL_error_printf ("Pattern for stdout label specified by MPIEXEC_PREFIX_STROUT is too long"); } } envval = getenv("MPIEXEC_PREFIX_STDERR"); if (envval) { if (strlen(envval) < MAX_LABEL) { MPL_strncpy(errLabelPattern, envval, MAX_LABEL); useLabels = 1; } else { MPL_error_printf ("Pattern for stderr label specified by MPIEXEC_PREFIX_STRERR is too long"); } } return 0; }