1 /*
2  * Copyright (C) by Argonne National Laboratory
3  *     See COPYRIGHT in top-level directory
4  */
5 
6 /*
7  * This file provides a set of routines that can be used to record debug
8  * messages in a ring so that the may be dumped at a later time.  For example,
9  * this can be used to record debug messages without printing them.
10  */
11 
12 #include "mpl.h"
13 
14 #ifdef MPL_HAVE_UNISTD_H
15 #include <unistd.h>
16 #endif
17 #ifdef MPL_HAVE_ERRNO_H
18 #include <errno.h>
19 #endif
20 
21 #if defined(MPL_HAVE_MKSTEMP) && defined(MPL_NEEDS_MKSTEMP_DECL)
22 extern int mkstemp(char *t);
23 #endif
24 
25 #if defined(MPL_HAVE_FDOPEN) && defined(MPL_NEEDS_FDOPEN_DECL)
26 extern FILE *fdopen(int fd, const char *mode);
27 #endif
28 
29 #ifdef MPL_USE_DBG_LOGGING
30 
31 #ifndef MAXPATHLEN
32 #define MAXPATHLEN 1024
33 #endif
34 
35 int MPL_dbg_active_classes = 0;
36 int MPL_dbg_max_level = MPL_DBG_TYPICAL;
37 
38 static enum {
39     DBG_UNINIT,
40     DBG_PREINIT,
41     DBG_INITIALIZED,
42     DBG_ERROR
43 } dbg_initialized = DBG_UNINIT;
44 
45 static char file_pattern_buf[MAXPATHLEN] = "";
46 static const char *file_pattern = "-stdout-";   /* "log%d.log"; */
47 static const char *default_file_pattern = "dbg@W%w-@%d@T-%t@.log";
48 static char temp_filename[MAXPATHLEN] = "";
49 static int world_num = 0;
50 static int world_rank = -1;
51 static int which_rank = -1;     /* all ranks */
52 static double time_origin = 0.0;
53 
54 static int dbg_usage(const char *, const char *);
55 static int dbg_openfile(FILE ** dbg_fp);
56 static int dbg_set_class(const char *);
57 static int dbg_set_level(const char *, const char *(names[]));
58 static int dbg_get_filename(char *filename, int len);
59 
60 #if (MPL_THREAD_PACKAGE_NAME != MPL_THREAD_PACKAGE_NONE)
61 static MPL_thread_tls_key_t dbg_tls_key;
62 #endif
63 
64 static FILE *dbg_static_fp = 0;
65 
66 /*
67  * This function finds the basename in a path (ala "man 1 basename").
68  * *basename will point to an element in path.
69  * More formally: This function sets basename to the character just
70  * after the last '/' in path.
71 */
72 static void find_basename(char *path, char **basename) ATTRIBUTE((unused));
find_basename(char * path,char ** basename)73 static void find_basename(char *path, char **basename)
74 {
75     char *c;
76 
77     c = *basename = path;
78     while (*c) {
79         if (*c == '/')
80             *basename = c + 1;
81         ++c;
82     }
83 }
84 
dbg_init_tls(void)85 static int dbg_init_tls(void)
86 {
87     int err = 0;
88 
89 #if (MPL_THREAD_PACKAGE_NAME != MPL_THREAD_PACKAGE_NONE)
90     MPL_thread_tls_create(NULL, &dbg_tls_key, &err);
91 #endif
92 
93     return err;
94 }
95 
get_fp(void)96 static FILE *get_fp(void)
97 {
98 #if (MPL_THREAD_PACKAGE_NAME != MPL_THREAD_PACKAGE_NONE)
99     int err;
100     /* if we're not initialized, use the static fp, since there should
101      * only be one thread in here until then */
102     if (dbg_initialized == DBG_INITIALIZED) {
103         FILE *fp;
104         MPL_thread_tls_get(&dbg_tls_key, (void **) &fp, &err);
105         return fp;
106     }
107 #endif
108 
109     return dbg_static_fp;
110 }
111 
set_fp(FILE * fp)112 static void set_fp(FILE * fp)
113 {
114 #if (MPL_THREAD_PACKAGE_NAME != MPL_THREAD_PACKAGE_NONE)
115     int err;
116     /* if we're not initialized, use the static fp, since there should
117      * only be one thread in here until then */
118     if (dbg_initialized == DBG_INITIALIZED) {
119         MPL_thread_tls_set(&dbg_tls_key, (void *) fp, &err);
120         return;
121     }
122 #endif
123 
124     dbg_static_fp = fp;
125 }
126 
MPL_dbg_outevent(const char * file,int line,int class,int kind,const char * fmat,...)127 int MPL_dbg_outevent(const char *file, int line, int class, int kind, const char *fmat, ...)
128 {
129     int mpl_errno = MPL_SUCCESS;
130     va_list list;
131     char *str, stmp[MPL_DBG_MAXLINE];
132     int i;
133     void *p;
134     MPL_time_t t;
135     double curtime;
136     unsigned long long int threadID = 0;
137     int pid = -1;
138     FILE *dbg_fp = NULL;
139 
140     if (dbg_initialized == DBG_UNINIT || dbg_initialized == DBG_ERROR)
141         goto fn_exit;
142 
143     dbg_fp = get_fp();
144 
145 #if (MPL_THREAD_PACKAGE_NAME != MPL_THREAD_PACKAGE_NONE)
146     {
147         /* the thread ID is not necessarily unique between processes, so a
148          * (pid,tid) pair should be used to uniquely identify output from
149          * particular threads on a system */
150         MPL_thread_id_t tid;
151         MPL_thread_self(&tid);
152         threadID = (unsigned long long int) tid;
153     }
154 #endif
155 #if defined(MPL_HAVE_GETPID)
156     pid = (int) getpid();
157 #endif /* MPL_HAVE_GETPID */
158 
159     if (!dbg_fp) {
160         mpl_errno = dbg_openfile(&dbg_fp);
161         if (mpl_errno)
162             goto fn_fail;
163         set_fp(dbg_fp);
164     }
165 
166     MPL_wtime(&t);
167     MPL_wtime_todouble(&t, &curtime);
168     curtime = curtime - time_origin;
169 
170     /* The kind values are used with the macros to simplify these cases */
171     switch (kind) {
172         case 0:
173             va_start(list, fmat);
174             str = va_arg(list, char *);
175             fprintf(dbg_fp, "%d\t%d\t%llx[%d]\t%d\t%f\t%s\t%d\t%s\n",
176                     world_num, world_rank, threadID, pid, class, curtime, file, line, str);
177             va_end(list);
178             break;
179         case 1:
180             va_start(list, fmat);
181             str = va_arg(list, char *);
182             MPL_snprintf(stmp, sizeof(stmp), fmat, str);
183             va_end(list);
184             fprintf(dbg_fp, "%d\t%d\t%llx[%d]\t%d\t%f\t%s\t%d\t%s\n",
185                     world_num, world_rank, threadID, pid, class, curtime, file, line, stmp);
186             break;
187         case 2:
188             va_start(list, fmat);
189             i = va_arg(list, int);
190             MPL_snprintf(stmp, sizeof(stmp), fmat, i);
191             va_end(list);
192             fprintf(dbg_fp, "%d\t%d\t%llx[%d]\t%d\t%f\t%s\t%d\t%s\n",
193                     world_num, world_rank, threadID, pid, class, curtime, file, line, stmp);
194             break;
195         case 3:
196             va_start(list, fmat);
197             p = va_arg(list, void *);
198             MPL_snprintf(stmp, sizeof(stmp), fmat, p);
199             va_end(list);
200             fprintf(dbg_fp, "%d\t%d\t%llx[%d]\t%d\t%f\t%s\t%d\t%s\n",
201                     world_num, world_rank, threadID, pid, class, curtime, file, line, stmp);
202             break;
203         default:
204             break;
205     }
206     fflush(dbg_fp);
207 
208   fn_exit:
209   fn_fail:
210     return 0;
211 }
212 
213 /* These are used to simplify the handling of options.
214    To add a new name, add an dbg_classname element to the array
215    classnames.  The "classbits" values are defined by MPL_DBG_CLASS
216    in mpl_dbg.h
217  */
218 
219 typedef struct dbg_classname {
220     int classbits;
221     const char *ucname, *lcname;
222 } dbg_classname;
223 
224 #define MAX_DBG_CLASSNAMES (sizeof(unsigned int) * 8)
225 
226 static dbg_classname classnames[MAX_DBG_CLASSNAMES];
227 static int num_classnames = 0;
228 
229 static const char *unregistered_classes[MAX_DBG_CLASSNAMES];
230 static int num_unregistered_classes = 0;
231 
232 /* Because the level values are simpler and are rarely changed, these
233  * use a simple set of parallel arrays */
234 static const int level_values[] = {
235     MPL_DBG_TERSE,
236     MPL_DBG_TYPICAL,
237     MPL_DBG_VERBOSE,
238     100
239 };
240 static const char *level_name[] = { "TERSE", "TYPICAL", "VERBOSE", 0 };
241 static const char *lc_level_name[] = { "terse", "typical", "verbose", 0 };
242 
MPL_dbg_class_register(MPL_dbg_class class,const char * ucname,const char * lcname)243 void MPL_dbg_class_register(MPL_dbg_class class, const char *ucname, const char *lcname)
244 {
245     int i, j;
246 
247     classnames[num_classnames].classbits = class;
248     classnames[num_classnames].ucname = ucname;
249     classnames[num_classnames].lcname = lcname;
250     num_classnames++;
251 
252     if (num_unregistered_classes) {
253         /* there are some unregistered classes.  look through to see
254          * if any of them match this class. */
255         size_t len = strlen(lcname);
256 
257         for (i = 0; i < num_unregistered_classes; i++) {
258             size_t slen = strlen(unregistered_classes[i]);
259             if (len == slen && (!strncmp(unregistered_classes[i], lcname, len) ||
260                                 !strncmp(unregistered_classes[i], ucname, len))) {
261                 /* got a match */
262                 MPL_dbg_active_classes |= class;
263                 for (j = i; j < num_unregistered_classes - 1; j++)
264                     unregistered_classes[j] = unregistered_classes[j + 1];
265                 num_unregistered_classes--;
266                 break;
267             }
268         }
269     }
270 }
271 
MPL_dbg_class_alloc(const char * ucname,const char * lcname)272 MPL_dbg_class MPL_dbg_class_alloc(const char *ucname, const char *lcname)
273 {
274     static unsigned int class = 1;
275 
276     /* create a user handle for this class */
277     MPL_dbg_class_register(class, ucname, lcname);
278 
279     class <<= 1;
280 
281     return (class >> 1);
282 }
283 
284 /*
285  * Initialize the DBG_MSG system.  This is called during the job
286  * initialization to process command-line arguments as well as
287  * checking either the MPICH_DBG or MPL_DBG environment variables.
288  * The initialization
289  * is split into two steps: a preinit and an init. This makes it
290  * possible to enable most of the features before the full
291  * initialization, where a significant amount of the initialization
292  * takes place.
293  */
294 
dbg_process_args(int * argc_p,char *** argv_p)295 static int dbg_process_args(int *argc_p, char ***argv_p)
296 {
297     int i, rc;
298 
299     /* Here's where we do the same thing with the command-line options */
300     if (argc_p) {
301         for (i = 1; i < *argc_p; i++) {
302             if (strncmp((*argv_p)[i], "-mpich-dbg", 10) == 0) {
303                 char *s = (*argv_p)[i] + 10;
304                 /* Found a command */
305                 if (*s == 0) {
306                     /* Just -mpich-dbg */
307                     MPL_dbg_max_level = MPL_DBG_TYPICAL;
308                     MPL_dbg_active_classes = MPL_DBG_ALL;
309                 } else if (*s == '=') {
310                     /* look for file */
311                     MPL_dbg_max_level = MPL_DBG_TYPICAL;
312                     MPL_dbg_active_classes = MPL_DBG_ALL;
313                     s++;
314                     if (strncmp(s, "file", 4) == 0) {
315                         file_pattern = default_file_pattern;
316                     }
317                 } else if (strncmp(s, "-level", 6) == 0) {
318                     char *p = s + 6;
319                     if (*p == '=') {
320                         p++;
321                         rc = dbg_set_level(p, lc_level_name);
322                         if (rc)
323                             dbg_usage("-mpich-dbg-level", "terse, typical, verbose");
324                     }
325                 } else if (strncmp(s, "-class", 6) == 0) {
326                     char *p = s + 6;
327                     if (*p == '=') {
328                         p++;
329                         rc = dbg_set_class(p);
330                         if (rc)
331                             dbg_usage("-mpich-dbg-class", 0);
332                     }
333                 } else if (strncmp(s, "-filename", 9) == 0) {
334                     char *p = s + 9;
335                     if (*p == '=') {
336                         p++;
337                         /* A special case for a filepattern of "-default",
338                          * use the predefined default pattern */
339                         if (strcmp(p, "-default") == 0) {
340                             file_pattern = default_file_pattern;
341                         } else {
342                             strncpy(file_pattern_buf, p, sizeof(file_pattern_buf) - 1);
343 
344                             /* Make sure the string is NULL-terminated */
345                             file_pattern_buf[MAXPATHLEN - 1] = '\0';
346 
347                             file_pattern = file_pattern_buf;
348                         }
349                     }
350                 } else if (strncmp(s, "-rank", 5) == 0) {
351                     char *p = s + 5;
352                     if (*p == '=' && p[1] != 0) {
353                         char *sOut;
354                         p++;
355                         which_rank = (int) strtol(p, &sOut, 10);
356                         if (p == sOut) {
357                             dbg_usage("-mpich-dbg-rank", 0);
358                             which_rank = -1;
359                         }
360                     }
361                 } else {
362                     dbg_usage((*argv_p)[i], 0);
363                 }
364 
365                 /* Eventually, should null it out and reduce argc value */
366             }
367         }
368     }
369     return MPL_SUCCESS;
370 }
371 
372 /* could two different environment variables control the same thing?  sure they
373  * could! consider MPICH: we moved all our logging code into MPL, so it should
374  * have an MPL_ prefix, but all the documentation assumes an "MPICH_" prefix.
375  * So we'll look for both. */
getenv_either(const char * env_a,const char * env_b)376 static char *getenv_either(const char *env_a, const char *env_b)
377 {
378     char *s;
379     if ((s = getenv(env_a)) == NULL)
380         s = getenv(env_b);
381 
382     return s;
383 }
384 
385 
dbg_process_env(void)386 static int dbg_process_env(void)
387 {
388     char *s;
389     int rc;
390 
391     s = getenv_either("MPICH_DBG", "MPL_DBG");
392     if (s) {
393         /* Set the defaults */
394         MPL_dbg_max_level = MPL_DBG_TYPICAL;
395         MPL_dbg_active_classes = MPL_DBG_ALL;
396         if (strncmp(s, "FILE", 4) == 0) {
397             file_pattern = default_file_pattern;
398         }
399     }
400     s = getenv_either("MPICH_DBG_LEVEL", "MPL_DBG_LEVEL");
401     if (s) {
402         rc = dbg_set_level(s, level_name);
403         if (rc)
404             dbg_usage("MPL_DBG_LEVEL", "TERSE, TYPICAL, VERBOSE");
405     }
406 
407     s = getenv_either("MPICH_DBG_CLASS", "MPL_DBG_CLASS");
408     if (s) {
409         rc = dbg_set_class(s);
410         if (rc)
411             dbg_usage("MPL_DBG_CLASS", 0);
412     }
413 
414     s = getenv_either("MPICH_DBG_FILENAME", "MPL_DBG_FILENAME");
415     if (s) {
416         strncpy(file_pattern_buf, s, sizeof(file_pattern_buf) - 1);
417 
418         /* Make sure the string is NULL-terminated */
419         file_pattern_buf[MAXPATHLEN - 1] = '\0';
420 
421         file_pattern = file_pattern_buf;
422     }
423 
424     s = getenv_either("MPICH_DBG_RANK", "MPL_DBG_RANK");
425     if (s) {
426         char *sOut;
427         which_rank = (int) strtol(s, &sOut, 10);
428         if (s == sOut) {
429             dbg_usage("MPL_DBG_RANK", 0);
430             which_rank = -1;
431         }
432     }
433     return MPL_SUCCESS;
434 }
435 
436 MPL_dbg_class MPL_DBG_ROUTINE_ENTER;
437 MPL_dbg_class MPL_DBG_ROUTINE_EXIT;
438 MPL_dbg_class MPL_DBG_ROUTINE;
439 MPL_dbg_class MPL_DBG_ALL = ~(0);       /* pre-initialize the ALL class */
440 
441 /*
442  * Attempt to initialize the logging system.  This works only if the
443  * full initialization is not required for updating the environment
444  * and/or command-line arguments.
445  */
MPL_dbg_pre_init(int * argc_p,char *** argv_p)446 int MPL_dbg_pre_init(int *argc_p, char ***argv_p)
447 {
448     MPL_time_t t;
449 
450     /* if the DBG_MSG system was already initialized, say by the
451      * device, then return immediately */
452     if (dbg_initialized != DBG_UNINIT)
453         return MPL_SUCCESS;
454 
455     if (dbg_init_tls())
456         return MPL_ERR_DBG_OTHER;
457 
458     /* Check to see if any debugging was selected.  The order of these
459      * tests is important, as they allow general defaults to be set,
460      * followed by more specific modifications */
461     /* First, the environment variables */
462     dbg_process_env();
463 
464     dbg_process_args(argc_p, argv_p);
465 
466     MPL_wtime_init();
467     MPL_wtime(&t);
468     MPL_wtime_todouble(&t, &time_origin);
469 
470     /* Allocate the predefined classes */
471     MPL_DBG_ROUTINE_ENTER = MPL_dbg_class_alloc("ROUTINE_ENTER", "routine_enter");
472     MPL_DBG_ROUTINE_EXIT = MPL_dbg_class_alloc("ROUTINE_EXIT", "routine_exit");
473 
474     MPL_DBG_CLASS_CLR(MPL_DBG_ROUTINE);
475     MPL_DBG_CLASS_APPEND(MPL_DBG_ROUTINE, MPL_DBG_ROUTINE_ENTER);
476     MPL_DBG_CLASS_APPEND(MPL_DBG_ROUTINE, MPL_DBG_ROUTINE_EXIT);
477     MPL_dbg_class_register(MPL_DBG_ROUTINE, "ROUTINE", "routine");
478 
479     MPL_dbg_class_register(MPL_DBG_ALL, "ALL", "all");
480 
481     dbg_initialized = DBG_PREINIT;
482 
483     return MPL_SUCCESS;
484 }
485 
MPL_dbg_init(int wnum,int wrank)486 int MPL_dbg_init(int wnum, int wrank)
487 {
488     int ret;
489     FILE *dbg_fp = NULL;
490 
491     /* if the DBG_MSG system was already initialized, say by the
492      * device, then return immediately.  Note that the device is then
493      * responsible for handling the file mode (e.g., reopen when the
494      * rank become available) */
495     if (dbg_initialized == DBG_INITIALIZED || dbg_initialized == DBG_ERROR)
496         return MPL_SUCCESS;
497 
498     if (dbg_initialized != DBG_PREINIT) {
499         if (dbg_init_tls())
500             return MPL_ERR_DBG_OTHER;
501     }
502 
503     dbg_fp = get_fp();
504 
505     world_num = wnum;
506     world_rank = wrank;
507 
508     if (which_rank >= 0 && which_rank != wrank) {
509         /* Turn off logging on this process */
510         MPL_dbg_active_classes = 0;
511     }
512 
513     /* If the file has already been opened with a temp filename,
514      * rename it. */
515     if (dbg_fp && dbg_fp != stdout && dbg_fp != stderr) {
516         char filename[MAXPATHLEN] = "";
517 
518         dbg_get_filename(filename, MAXPATHLEN);
519         ret = rename(temp_filename, filename);
520         if (ret) {
521             /* Retry renaming file after closing it */
522             fclose(dbg_fp);
523             ret = rename(temp_filename, filename);
524             if (ret) {
525                 MPL_error_printf("Could not rename temp log file to %s\n", filename);
526                 goto fn_fail;
527             } else {
528                 dbg_fp = fopen(filename, "a+");
529                 set_fp(dbg_fp);
530                 if (dbg_fp == NULL) {
531                     MPL_error_printf("Error re-opening log file, %s\n", filename);
532                     goto fn_fail;
533                 }
534             }
535         }
536     }
537 
538     dbg_initialized = DBG_INITIALIZED;
539 
540     /* updating dbg_initialized may alter where dbg_fp is stored, set it again */
541     set_fp(dbg_fp);
542 
543   fn_exit:
544     return MPL_SUCCESS;
545   fn_fail:
546     dbg_initialized = DBG_ERROR;
547     goto fn_exit;
548 }
549 
550 /* Print the usage statement to stderr */
dbg_usage(const char * cmd,const char * vals)551 static int dbg_usage(const char *cmd, const char *vals)
552 {
553     if (vals) {
554         fprintf(stderr, "Incorrect value for %s, should be one of %s\n", cmd, vals);
555     } else {
556         fprintf(stderr, "Incorrect value for %s\n", cmd);
557     }
558     fprintf(stderr, "Command line for debug switches\n\
559     -mpich-dbg-class=name[,name,...]\n\
560     -mpich-dbg-level=name   (one of terse, typical, verbose)\n\
561     -mpich-dbg-filename=pattern (includes %%d for world rank, %%t for thread id\n\
562     -mpich-dbg-rank=val    (only this rank in COMM_WORLD will be logged)\n\
563     -mpich-dbg   (shorthand for -mpich-dbg-class=all -mpich-dbg-level=typical)\n\
564     -mpich-dbg=file (shorthand for -mpich-dbg -mpich-dbg-filename=%s)\n\
565 Environment variables\n\
566     MPICH_DBG_CLASS=NAME[,NAME...]\n\
567     MPICH_DBG_LEVEL=NAME\n\
568     MPICH_DBG_FILENAME=pattern\n\
569     MPICH_DBG_RANK=val\n\
570     MPICH_DBG=YES or FILE\n", default_file_pattern);
571 
572     fflush(stderr);
573 
574     return 0;
575 }
576 
577 #if defined (MPL_HAVE_MKSTEMP) && defined (MPL_HAVE_FDOPEN)
578 
579 /* creates a temporary file in the same directory the user specified
580  * for the log file */
dbg_open_tmpfile(FILE ** dbg_fp)581 static int dbg_open_tmpfile(FILE ** dbg_fp)
582 {
583     int mpl_errno = MPL_SUCCESS;
584     const char temp_pattern[] = "templogXXXXXX";
585     int fd;
586     char *basename;
587     int ret;
588 
589     ret = MPL_strncpy(temp_filename, file_pattern, MAXPATHLEN);
590     if (ret)
591         goto fn_fail;
592 
593     find_basename(temp_filename, &basename);
594 
595     /* make sure there's enough room in temp_filename to store temp_pattern */
596     if (basename - temp_filename > MAXPATHLEN - sizeof(temp_pattern))
597         goto fn_fail;
598 
599     MPL_strncpy(basename, temp_pattern, sizeof(temp_pattern));
600 
601     fd = mkstemp(temp_filename);
602     if (fd == -1)
603         goto fn_fail;
604 
605     *dbg_fp = fdopen(fd, "a+");
606     if (*dbg_fp == NULL)
607         goto fn_fail;
608 
609   fn_exit:
610     return mpl_errno;
611   fn_fail:
612     MPL_error_printf("Could not open log file %s\n", temp_filename);
613     dbg_initialized = DBG_ERROR;
614     mpl_errno = MPL_ERR_DBG_INTERN;
615     goto fn_exit;
616 }
617 
618 #elif defined(MPL_HAVE__MKTEMP_S) && defined(MPL_HAVE_FOPEN_S)
619 
620 /* creates a temporary file in the same directory the user specified
621  * for the log file */
dbg_open_tmpfile(FILE ** dbg_fp)622 static int dbg_open_tmpfile(FILE ** dbg_fp)
623 {
624     int mpl_errno = MPL_SUCCESS;
625     const char temp_pattern[] = "templogXXXXXX";
626     int fd;
627     char *basename;
628     int ret;
629     errno_t ret_errno;
630 
631     ret = MPL_strncpy(temp_filename, file_pattern, MAXPATHLEN);
632     if (ret)
633         goto fn_fail;
634 
635     find_basename(temp_filename, &basename);
636 
637     /* make sure there's enough room in temp_filename to store temp_pattern */
638     if (basename - temp_filename > MAXPATHLEN - sizeof(temp_pattern))
639         goto fn_fail;
640 
641     MPL_strncpy(basename, temp_pattern, sizeof(temp_pattern));
642 
643     ret_errno = _mktemp_s(temp_filename, MAXPATHLEN);
644     if (ret_errno != 0)
645         goto fn_fail;
646 
647     ret_errno = fopen_s(dbg_fp, temp_filename, "a+");
648     if (ret_errno != 0)
649         goto fn_fail;
650 
651   fn_exit:
652     return mpl_errno;
653   fn_fail:
654     MPL_error_printf("Could not open log file %s\n", temp_filename);
655     dbg_initialized = DBG_ERROR;
656     mpl_errno = MPL_ERR_DBG_INTERN;
657     goto fn_exit;
658 }
659 
660 #else
661 
662 /* creates a temporary file in some directory, which may not be where
663  * the user wants the log file.  When the file is renamed later, it
664  * may require a copy.
665  *
666  * Note that this is not safe: By the time we call fopen(), another
667  * file with the same name may exist.  That file would get clobbered.
668 */
dbg_open_tmpfile(FILE ** dbg_fp)669 static int dbg_open_tmpfile(FILE ** dbg_fp)
670 {
671     int mpl_errno = MPL_SUCCESS;
672     char *cret;
673 
674     cret = tmpnam(temp_filename);
675     if (cret == NULL)
676         goto fn_fail;
677 
678     *dbg_fp = fopen(temp_filename, "w");
679     if (*dbg_fp == NULL)
680         goto fn_fail;
681 
682   fn_exit:
683     return mpl_errno;
684   fn_fail:
685     MPL_error_printf("Could not open log file %s\n", temp_filename);
686     dbg_initialized = DBG_ERROR;
687     mpl_errno = MPL_ERR_DBG_INTERN;
688     goto fn_exit;
689 }
690 
691 #endif
692 
693 /* This routine can make no MPI calls, since it may be logging those
694  * calls. */
dbg_get_filename(char * filename,int len)695 static int dbg_get_filename(char *filename, int len)
696 {
697     int withinMworld = 0,       /* True if within an @W...@ */
698         withinMthread = 0;      /* True if within an @T...@ */
699     /* FIXME: Need to know how many process groups are known */
700 #if (MPL_THREAD_PACKAGE_NAME != MPL_THREAD_PACKAGE_NONE)
701     unsigned long long int threadID = 0;
702     int nThread = 2;
703 #else
704     int nThread = 1;
705 #endif
706     static char world_numAsChar[10] = "0";
707     char *pDest;
708     const char *p;
709 
710     if (world_num == 1) {
711         world_numAsChar[0] = '1';
712         world_numAsChar[1] = '\0';
713     }
714 
715     p = file_pattern;
716     pDest = filename;
717     *filename = 0;
718     while (*p && (pDest - filename) < len - 1) {
719         /* There are two special cases that allow text to
720          * be optionally included.  Those patterns are
721          * @T...@ (only if multi-threaded) and
722          * @W...@ (only if more than one process group)
723          * UNIMPLEMENTED/UNTESTED */
724         if (*p == '@') {
725             /* Escaped @? */
726             if (p[1] == '@') {
727                 *pDest++ = *++p;
728                 continue;
729             }
730             /* If within an @...@, terminate it */
731             if (withinMworld) {
732                 withinMworld = 0;
733                 p++;
734             } else if (withinMthread) {
735                 withinMthread = 0;
736                 p++;
737             } else {
738                 /* Look for command */
739                 p++;
740                 if (*p == 'W') {
741                     p++;
742                     withinMworld = 1;
743                 } else if (*p == 'T') {
744                     p++;
745                     withinMthread = 1;
746                 } else {
747                     /* Unrecognized char */
748                     *pDest++ = *p++;
749                 }
750             }
751         } else if ((withinMworld && world_num == 0) || (withinMthread && nThread == 1)) {
752             /* Simply skip this character since we're not showing
753              * this string */
754             p++;
755         } else if (*p == '%') {
756             p++;
757             if (*p == 'd') {
758                 char rankAsChar[20];
759                 MPL_snprintf(rankAsChar, sizeof(rankAsChar), "%d", world_rank);
760                 *pDest = 0;
761                 MPL_strnapp(filename, rankAsChar, len);
762                 pDest += strlen(rankAsChar);
763             } else if (*p == 't') {
764 #if (MPL_THREAD_PACKAGE_NAME != MPL_THREAD_PACKAGE_NONE)
765                 char threadIDAsChar[30];
766                 MPL_thread_id_t tid;
767                 MPL_thread_self(&tid);
768                 threadID = (unsigned long long int) tid;
769 
770                 MPL_snprintf(threadIDAsChar, sizeof(threadIDAsChar), "%llx", threadID);
771                 *pDest = 0;
772                 MPL_strnapp(filename, threadIDAsChar, len);
773                 pDest += strlen(threadIDAsChar);
774 #else
775                 *pDest++ = '0';
776 #endif /* MPL_THREAD_PACKAGE_NAME != MPL_THREAD_PACKAGE_NONE */
777             } else if (*p == 'w') {
778                 /* *pDest++ = '0'; */
779                 *pDest = 0;
780                 MPL_strnapp(filename, world_numAsChar, len);
781                 pDest += strlen(world_numAsChar);
782             } else if (*p == 'p') {
783                 /* Appends the pid of the proceess to the file name. */
784                 char pidAsChar[20];
785 #if defined(MPL_HAVE_GETPID)
786                 pid_t pid = getpid();
787 #else
788                 int pid = -1;
789 #endif /* MPL_HAVE_GETPID */
790                 MPL_snprintf(pidAsChar, sizeof(pidAsChar), "%d", (int) pid);
791                 *pDest = 0;
792                 MPL_strnapp(filename, pidAsChar, len);
793                 pDest += strlen(pidAsChar);
794             } else {
795                 *pDest++ = '%';
796                 *pDest++ = *p;
797             }
798             p++;
799         } else {
800             *pDest++ = *p++;
801         }
802     }
803     *pDest = 0;
804 
805     return 0;
806 }
807 
808 /* This routine can make no MPI calls, since it may be logging those
809  * calls. */
dbg_openfile(FILE ** dbg_fp)810 static int dbg_openfile(FILE ** dbg_fp)
811 {
812     int mpl_errno = MPL_SUCCESS;
813     if (!file_pattern || *file_pattern == 0 || strcmp(file_pattern, "-stdout-") == 0) {
814         *dbg_fp = stdout;
815     } else if (strcmp(file_pattern, "-stderr-") == 0) {
816         *dbg_fp = stderr;
817     } else {
818         char filename[MAXPATHLEN];
819 
820         /* if we're not at DBG_INITIALIZED, we don't know our
821          * rank yet, so we create a temp file, to be renamed later */
822         if (dbg_initialized != DBG_INITIALIZED) {
823             mpl_errno = dbg_open_tmpfile(dbg_fp);
824             if (mpl_errno)
825                 goto fn_fail;
826         } else {
827             mpl_errno = dbg_get_filename(filename, MAXPATHLEN);
828             if (mpl_errno)
829                 goto fn_fail;
830 
831             *dbg_fp = fopen(filename, "w");
832             if (!*dbg_fp) {
833                 MPL_error_printf("Could not open log file %s\n", filename);
834                 goto fn_fail;
835             }
836         }
837     }
838   fn_exit:
839     return mpl_errno;
840   fn_fail:
841     dbg_initialized = DBG_ERROR;
842     mpl_errno = MPL_ERR_DBG_INTERN;
843     goto fn_exit;
844 }
845 
846 /* Support routines for processing mpich-dbg values */
847 /* Update the GLOBAL variable MPL_dbg_active_classes with the bits
848  * corresponding to this name */
dbg_set_class(const char * s)849 static int dbg_set_class(const char *s)
850 {
851     int i, found_match;
852     size_t slen = 0;
853     char *str;
854 
855     if (s && *s)
856         slen = strlen(s);
857 
858     str = strtok((char *) s, ",");
859     while (str) {
860         found_match = 0;
861         for (i = 0; i < num_classnames; i++) {
862             size_t len = strlen(classnames[i].lcname);
863 
864             if (slen == len && (!strncmp(str, classnames[i].lcname, len) ||
865                                 !strncmp(str, classnames[i].ucname, len))) {
866                 /* we have a match */
867                 MPL_dbg_active_classes |= classnames[i].classbits;
868                 found_match = 1;
869                 break;
870             }
871         }
872 
873         if (!found_match) {
874             /* no match was found.  the component might not have
875              * registered yet.  store the user string for later
876              * access. */
877             unregistered_classes[num_unregistered_classes] = str;
878             num_unregistered_classes++;
879         }
880 
881         str = strtok(NULL, ",");
882     }
883 
884     return 0;
885 }
886 
887 /* Set the global MPL_dbg_max_level if there is a match with the known
888  * level names */
dbg_set_level(const char * s,const char * (names[]))889 static int dbg_set_level(const char *s, const char *(names[]))
890 {
891     int i;
892 
893     for (i = 0; names[i]; i++) {
894         if (strcmp(names[i], s) == 0) {
895             MPL_dbg_max_level = level_values[i];
896             return 0;
897         }
898     }
899     return 1;
900 }
901 #endif /* MPL_USE_DBG_LOGGING */
902