1 /*============================================================================
2 * Program logging information
3 *============================================================================*/
4
5 /*
6 This file is part of Code_Saturne, a general-purpose CFD tool.
7
8 Copyright (C) 1998-2021 EDF S.A.
9
10 This program is free software; you can redistribute it and/or modify it under
11 the terms of the GNU General Public License as published by the Free Software
12 Foundation; either version 2 of the License, or (at your option) any later
13 version.
14
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
18 details.
19
20 You should have received a copy of the GNU General Public License along with
21 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
22 Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 */
24
25 /*----------------------------------------------------------------------------*/
26
27 #include "cs_defs.h"
28
29 /*-----------------------------------------------------------------------------*/
30
31 /*----------------------------------------------------------------------------
32 * Standard C library headers
33 *----------------------------------------------------------------------------*/
34
35 #include <assert.h>
36 #include <errno.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40
41 /*----------------------------------------------------------------------------
42 * Local headers
43 *----------------------------------------------------------------------------*/
44
45 #include "bft_error.h"
46 #include "bft_mem.h"
47 #include "bft_printf.h"
48
49 #include "cs_base.h"
50 #include "cs_timer.h"
51
52 /*----------------------------------------------------------------------------
53 * Header for the current file
54 *----------------------------------------------------------------------------*/
55
56 #include "cs_log.h"
57
58 /*----------------------------------------------------------------------------*/
59
60 BEGIN_C_DECLS
61
62 /*! \cond DOXYGEN_SHOULD_SKIP_THIS */
63
64 /*-----------------------------------------------------------------------------
65 * Local type definitions
66 *-----------------------------------------------------------------------------*/
67
68 /*-----------------------------------------------------------------------------
69 * Local variable definitions
70 *-----------------------------------------------------------------------------*/
71
72 static bool _cs_log_atexit_set = false;
73
74 static FILE* _cs_log[] = {NULL, NULL, NULL};
75 static const char* _cs_log_name[] = {"",
76 "setup.log",
77 "performance.log"};
78
79 static bool _cs_log_default_active = true;
80
81 int cs_glob_log_frequency = 1;
82
83 /*============================================================================
84 * Prototypes for functions intended for use only by Fortran wrappers.
85 * (descriptions follow, with function bodies).
86 *============================================================================*/
87
88 void
89 cs_f_log_frequency_get_pointer(int **ntlist);
90
91 /*============================================================================
92 * Private function definitions
93 *============================================================================*/
94
95 /*----------------------------------------------------------------------------
96 * Close all log files.
97 *----------------------------------------------------------------------------*/
98
99 static void
_close_log_files(void)100 _close_log_files(void)
101 {
102 int i;
103
104 for (i = 0; i < CS_LOG_N_TYPES; i++) {
105 if (_cs_log[i] != NULL)
106 fclose(_cs_log[i]);
107 }
108 }
109
110 /*----------------------------------------------------------------------------
111 * Open log file.
112 *
113 * parameters:
114 * log <-- log file type
115 *----------------------------------------------------------------------------*/
116
117 static void
_open_log(cs_log_t log)118 _open_log(cs_log_t log)
119 {
120 if (cs_glob_rank_id < 1 && _cs_log[log] == NULL) {
121 _cs_log[log] = fopen(_cs_log_name[log], "w");
122 if (_cs_log[log] == NULL)
123 bft_error(__FILE__, __LINE__, errno,
124 _("Error opening log file: %s"),
125 _cs_log_name[log]);
126 if (_cs_log_atexit_set == false) {
127 atexit(_close_log_files);
128 _cs_log_atexit_set = true;
129 }
130 }
131 }
132
133 /*----------------------------------------------------------------------------*
134 * Pad a string so that its printable length is the required length.
135 *
136 * This allows pretty-printing with UTF-8 strings, whose actual length may be
137 * larger than their printable length in the presence of multibyte characters.
138 *
139 * If either the printable length of the string is longer than the target
140 * width or the actual length is long than the destination buffer's size,
141 * it is truncated.
142 *
143 * parameters:
144 * dest --> pointer to destination buffer
145 * str <-- pointer to printable string
146 * width <-- desired printed length
147 * destsize <-- destination buffer size
148 * align <-- 1: left, 0: right
149 *----------------------------------------------------------------------------*/
150
151 static void
_log_strpad(char * dest,const char * src,size_t width,size_t destsize,int align)152 _log_strpad(char *dest,
153 const char *src,
154 size_t width,
155 size_t destsize,
156 int align)
157 {
158 size_t i, j;
159 size_t pad_l = 0, pad_r = 0, p_len = 0, c_len = 0;
160 size_t _destsize = destsize - 1;
161
162 static int mode_utf8 = -1;
163
164 assert(dest != NULL && destsize > 0);
165
166 if (mode_utf8 == -1) {
167 char *lang = getenv("LANG");
168 mode_utf8 = 0;
169 if (lang != NULL) {
170 if ( strcmp(lang + strlen(lang) - 5, "UTF-8") == 0
171 || strcmp(lang + strlen(lang) - 4, "utf8") == 0)
172 mode_utf8 = 1;
173 }
174 }
175
176 if (src != NULL) {
177 if (mode_utf8 == 0) {
178 p_len = strlen(src);
179 if (p_len > _destsize)
180 p_len = _destsize;
181 c_len = p_len;
182 }
183 else { /* UTF-8 case */
184 for (i = 0;
185 i < _destsize && p_len < width;
186 i++) {
187 unsigned char c = src[i];
188 if (c == '\0') {
189 c_len = i;
190 break;
191 }
192 else if (c < 0x80 || c > 0xBF) { /* Single byte or first byte in UTF-8 */
193 p_len++;
194 c_len = i+1;
195 }
196 }
197 }
198 }
199
200 if (p_len < width && c_len < _destsize) {
201 size_t pad = width - p_len;
202 if (c_len + pad > _destsize)
203 pad = _destsize - c_len;
204 if (align == 0)
205 pad_r = pad;
206 else
207 pad_l = pad;
208 }
209
210 j = 0;
211 for (i = 0; i < pad_l; i++)
212 dest[j++] = ' ';
213 for (i = 0; i < c_len; i++)
214 dest[j++] = src[i];
215 for (i = 0; i < pad_r; i++)
216 dest[j++] = ' ';
217
218 dest[j] = '\0';
219 }
220
221 /*============================================================================
222 * Fortran wrapper function definitions
223 *============================================================================*/
224
225 /*----------------------------------------------------------------------------
226 * Get pointer to log frequency (ntlist in Fortran).
227 *
228 * This function is intended for use by Fortran wrappers, and
229 * enables mapping to Fortran global pointers.
230 *
231 * parameters:
232 * ntlist --> pointer to ntlist
233 *----------------------------------------------------------------------------*/
234
235 void
cs_f_log_frequency_get_pointer(int ** ntlist)236 cs_f_log_frequency_get_pointer(int **ntlist)
237 {
238 *ntlist = &cs_glob_log_frequency;
239 }
240
241 /*! (DOXYGEN_SHOULD_SKIP_THIS) \endcond */
242
243 /*============================================================================
244 * Public function definitions
245 *============================================================================*/
246
247 /*----------------------------------------------------------------------------*/
248 /*!
249 * \brief Update "active" or "inactive" flag of default log.
250 *
251 * This does not prevent output to the log file, but the flag can be queried
252 * using \ref cs_log_default_is_active, so in most cases, this status
253 * should be checked before logging output
254 *
255 * \param[in] activate true to activate, false to deactivate.
256 */
257 /*----------------------------------------------------------------------------*/
258
259 void
cs_log_default_activate(bool activate)260 cs_log_default_activate(bool activate)
261 {
262 _cs_log_default_active = activate;
263 }
264
265 /*----------------------------------------------------------------------------*/
266 /*!
267 * \brief Update "active" or "inactive" flag of default log.
268 *
269 * This does not prevent output to the log file, but the flag can be queried
270 * using \
271 *
272 * \return true if active, false otherwise.
273 */
274 /*----------------------------------------------------------------------------*/
275
276 bool
cs_log_default_is_active(void)277 cs_log_default_is_active(void)
278 {
279 return _cs_log_default_active;
280 }
281
282 /*----------------------------------------------------------------------------*/
283 /*!
284 * \brief Count printable length of a character string.
285 *
286 * This should also include UTF-8 strings.
287 *
288 * \param[in] str pointer to printable string
289 *
290 * \return printable length of character string.
291 */
292 /*----------------------------------------------------------------------------*/
293
294 size_t
cs_log_strlen(const char * str)295 cs_log_strlen(const char *str)
296 {
297 static int mode_utf8 = -1;
298
299 int l = 0;
300 int retval = 0;
301
302 if (mode_utf8 == -1) {
303 char *lang = getenv("LANG");
304 mode_utf8 = 0;
305 if (lang != NULL) {
306 if ( strcmp(lang + strlen(lang) - 5, "UTF-8") == 0
307 || strcmp(lang + strlen(lang) - 4, "utf8") == 0)
308 mode_utf8 = 1;
309 }
310 }
311
312 if (str != NULL) {
313
314 l = strlen(str);
315
316 if (mode_utf8 == 0)
317 retval = l;
318
319 else if (mode_utf8 == 1) {
320
321 int i;
322 bool multibyte = false;
323
324 for (i = 0; i < l; i++) {
325
326 char c = str[i];
327
328 if (multibyte == false || (c < 0x80 || c > 0xBF)) {
329
330 multibyte = false;
331
332 if (c <= 0x7F) {
333 retval++;
334 }
335 else {
336 multibyte = true;
337 retval++;
338 }
339 }
340 }
341 }
342 }
343
344 return retval;
345 }
346
347 /*----------------------------------------------------------------------------*/
348 /*!
349 * \brief Pad a string so that its printable length is the required length.
350 *
351 * This allows pretty-printing with UTF-8 strings, whose actual length may be
352 * larger than their printable length in the presence of multibyte characters.
353 *
354 * If either the printable length of the string is longer than the target
355 * width or the actual length is long than the destination buffer's size,
356 * it is truncated.
357 *
358 * \param[out] dest pointer to destination buffer
359 * \param[in] src pointer to printable string
360 * \param[in] width desired printed length
361 * \param[in] destsize destination buffer size
362 */
363 /*----------------------------------------------------------------------------*/
364
365 void
cs_log_strpad(char * dest,const char * src,size_t width,size_t destsize)366 cs_log_strpad(char *dest,
367 const char *src,
368 size_t width,
369 size_t destsize)
370 {
371 _log_strpad(dest, src, width, destsize, 0);
372 }
373
374 /*----------------------------------------------------------------------------*/
375 /*!
376 * \brief Pad a string on the left so that its printable length is
377 * the required length.
378 *
379 * This allows pretty-printing with UTF-8 strings, whose actual length may be
380 * larger than their printable length in the presence of multibyte characters.
381 *
382 * If either the printable length of the string is longer than the target
383 * width or the actual length is long than the destination buffer's size,
384 * it is truncated.
385 *
386 * \param[out] dest pointer to destination buffer
387 * \param[in] src pointer to printable string
388 * \param[in] width desired printed length
389 * \param[in] destsize destination buffer size
390 */
391 /*----------------------------------------------------------------------------*/
392
393 void
cs_log_strpadl(char * dest,const char * src,size_t width,size_t destsize)394 cs_log_strpadl(char *dest,
395 const char *src,
396 size_t width,
397 size_t destsize)
398 {
399 _log_strpad(dest, src, width, destsize, 1);
400 }
401
402 /*----------------------------------------------------------------------------*/
403 /*!
404 * \brief Pretty-print int-32 based bit field to string
405 *
406 * \param[in] code value to print
407 * \param[out] buf output buffer (must be at least 33 bytes).
408 */
409 /*----------------------------------------------------------------------------*/
410
411 void
cs_log_binary_pp_int32(int32_t code,char buf[33])412 cs_log_binary_pp_int32(int32_t code,
413 char buf[33])
414 {
415 int i;
416 int32_t n = code;
417
418 for (i = 0; i < 33; i++)
419 buf[i] = ' ';
420 buf[32] = '\0';
421 buf[31] = '0';
422
423 i = 31;
424 while (n && i > -1) {
425 if (n & 1)
426 buf[i] = '1';
427 else
428 buf[i] = '0';
429 n = n >> 1;
430 i--;
431 }
432 }
433
434 /*----------------------------------------------------------------------------*/
435 /*!
436 * \brief Print log info to a given log type.
437 *
438 * The format and variable arguments are similar to those of the vprintf()
439 * type functions.
440 *
441 * In parallel, output is only handled by rank 0.
442 *
443 * \param[in] log log file type
444 * \param[in] format format string, as printf() and family.
445 * \param[in] arg_ptr variable arguments list pointer
446 *
447 * \return number of characters printed, not counting the trailing '\0' used
448 * to end output strings
449 */
450 /*----------------------------------------------------------------------------*/
451
452 int
cs_log_vprintf(cs_log_t log,const char * format,va_list arg_ptr)453 cs_log_vprintf(cs_log_t log,
454 const char *format,
455 va_list arg_ptr)
456 {
457 int retval;
458
459 if (cs_glob_rank_id > 0)
460 return 0;
461
462 if (log != CS_LOG_DEFAULT) {
463
464 if (_cs_log[log] == NULL && log != CS_LOG_DEFAULT)
465 _open_log(log);
466
467 retval = vfprintf(_cs_log[log], format, arg_ptr);
468
469 }
470
471 else {
472
473 bft_printf_proxy_t *_printf_proxy = bft_printf_proxy_get();
474
475 retval = _printf_proxy(format, arg_ptr);
476
477 }
478
479 return retval;
480 }
481
482 /*----------------------------------------------------------------------------*/
483 /*!
484 * \brief Print log info to a given log type.
485 *
486 * The format and variable arguments are similar to those of the printf()
487 * type functions.
488 *
489 * In parallel, output is only handled by rank 0.
490 *
491 * \param[in] log log file type
492 * \param[in] format format string, as printf() and family.
493 * \param[in] ... variable arguments based on format string.
494 *
495 * \return number of characters printed, not counting the trailing '\0' used
496 * to end output strings
497 */
498 /*----------------------------------------------------------------------------*/
499
500 int
cs_log_printf(cs_log_t log,const char * format,...)501 cs_log_printf(cs_log_t log,
502 const char *format,
503 ...)
504 {
505 int retval;
506 va_list arg_ptr;
507
508 if (cs_glob_rank_id > 0)
509 return 0;
510
511 if (log != CS_LOG_DEFAULT) {
512
513 if (_cs_log[log] == NULL && log != CS_LOG_DEFAULT)
514 _open_log(log);
515
516 va_start(arg_ptr, format);
517
518 retval = vfprintf(_cs_log[log], format, arg_ptr);
519
520 va_end(arg_ptr);
521
522 }
523
524 else {
525
526 bft_printf_proxy_t *_printf_proxy = bft_printf_proxy_get();
527
528 va_start(arg_ptr, format);
529
530 retval = _printf_proxy(format, arg_ptr);
531
532 va_end(arg_ptr);
533
534 }
535
536 return retval;
537 }
538
539 /*----------------------------------------------------------------------------*/
540 /*!
541 * \brief Flush output of a log file.
542 *
543 * In parallel, output is only handled by rank 0.
544 *
545 * If the argument is set to CS_LOG_N_TYPES, all log files are flushed.
546 *
547 * \param[in] log log file type
548 *
549 * \return 0 upon successful completion 0 is returned. Otherwise,
550 * EOF is returned and errno is set to indicate the error.
551 */
552 /*----------------------------------------------------------------------------*/
553
554 int
cs_log_printf_flush(cs_log_t log)555 cs_log_printf_flush(cs_log_t log)
556 {
557 int i;
558 int retval = 0;
559
560 if (log < CS_LOG_N_TYPES) {
561 if (log == CS_LOG_DEFAULT)
562 retval = bft_printf_flush();
563 else if (_cs_log[log] != NULL)
564 retval = fflush(_cs_log[log]);
565 }
566
567 else {
568 for (i = 0; i < CS_LOG_N_TYPES; i++) {
569 if (_cs_log[i] != NULL)
570 retval = fflush(_cs_log[i]);
571 if (retval != 0)
572 break;
573 }
574 retval = bft_printf_flush();
575 }
576
577 return retval;
578 }
579
580 /*----------------------------------------------------------------------------*/
581 /*!
582 * \brief Print a separator line in a log file
583 *
584 * In parallel, output is only handled by rank 0.
585 *
586 * \param[in] log log file type
587 */
588 /*----------------------------------------------------------------------------*/
589
590 void
cs_log_separator(cs_log_t log)591 cs_log_separator(cs_log_t log)
592 {
593 int i;
594 char separator[81];
595
596 for (i = 0; i < 80; i++)
597 separator[i] = '-';
598 separator[80] = '\0';
599
600 cs_log_printf(log, "%s\n", separator);
601 }
602
603 /*----------------------------------------------------------------------------*/
604 /*!
605 * \brief Output timing data array header to a given log.
606 *
607 * In parallel, output is only handled by rank 0.
608 *
609 * \param[in] log log file type
610 * \param[in] indent indentation before first column
611 * \param[in] header_title title for optional header line
612 * \param[in] calls true if calls column is to be used
613 */
614 /*----------------------------------------------------------------------------*/
615
616 void
cs_log_timer_array_header(cs_log_t log,int indent,const char * header_title,bool calls)617 cs_log_timer_array_header(cs_log_t log,
618 int indent,
619 const char *header_title,
620 bool calls)
621 {
622 int title_width = 80 - 16 - indent;
623 char tmp_s[4][64] = {"", "", "", ""};
624
625 /* Available width for title */
626
627 if (calls)
628 title_width -= 10; /* 1 field, 1 space + 9 digits */
629
630 /* Header line if requested */
631
632 assert(header_title != NULL);
633
634 if (strlen(header_title) > 0)
635 cs_log_strpad(tmp_s[0], _(header_title), title_width, 64);
636 else
637 cs_log_strpad(tmp_s[0], "", title_width, 64);
638
639 cs_log_strpadl(tmp_s[2], _("time"), 12, 64);
640
641 if (calls) {
642 cs_log_strpadl(tmp_s[1], _("calls"), 9, 64);
643 cs_log_printf(log,
644 "%*s%s %s %s\n",
645 indent, " ",
646 tmp_s[0], tmp_s[1], tmp_s[2]);
647 }
648 else
649 cs_log_printf(log,
650 "%*s%s %s\n",
651 indent, " ", tmp_s[0], tmp_s[2]);
652 }
653
654 /*----------------------------------------------------------------------------*/
655 /*!
656 * \brief Output timing data block to a given log.
657 *
658 * If the optional array of call counters is used, only lines
659 * with a number of calls greater than 0 are logged.
660 *
661 * In parallel, output is only handled by rank 0.
662 *
663 * \param[in] log log file type
664 * \param[in] indent indentation before first column
665 * \param[in] n_lines number of lines in array, excluding header
666 * \param[in] line_titles array of titles for data lines
667 * \param[in] calls optional array of call counters, or NULL
668 * \param[in] time_count array of time counters
669 */
670 /*----------------------------------------------------------------------------*/
671
672 void
cs_log_timer_array(cs_log_t log,int indent,int n_lines,const char * line_titles[],const unsigned calls[],const cs_timer_counter_t time_count[])673 cs_log_timer_array(cs_log_t log,
674 int indent,
675 int n_lines,
676 const char *line_titles[],
677 const unsigned calls[],
678 const cs_timer_counter_t time_count[])
679 {
680 int i;
681 int title_width = 80 - 16 - indent;
682 char tmp_s[4][64] = {"", "", "", ""};
683
684 /* Available width for title */
685
686 if (calls != NULL)
687 title_width -= 10; /* 1 field, 1 space + 9 digits */
688
689 /* Data lines */
690
691 for (i = 0; i < n_lines; i++) {
692 double wtime = (time_count[i]).nsec * 1.e-9;
693 if (line_titles != NULL)
694 cs_log_strpad(tmp_s[0], _(line_titles[i]), title_width, 64);
695 else
696 cs_log_strpad(tmp_s[0], "", title_width, 64);
697 if (calls != NULL) {
698 if (calls[i] > 0)
699 cs_log_printf(log,
700 "%*s%s %9u %12.3f\n",
701 indent, " ", tmp_s[0], calls[i], wtime);
702 }
703 else
704 cs_log_printf(log,
705 "%*s%s %12.3f\n",
706 indent, " ", tmp_s[0], wtime);
707 }
708 }
709
710 /*-----------------------------------------------------------------------------*/
711
712
713
714 END_C_DECLS
715