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