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