1 #include "cado.h" // IWYU pragma: keep
2 #include <cstdint>      // for SIZE_MAX
3 #include <cstring>      // for memset, strlen
4 #include <cstdio>       // fprintf // IWYU pragma: keep
5 #include <cstdarg>      // va_list // IWYU pragma: keep
6 #include <cstdlib>
7 #include <string>        // for string, basic_string
8 #include <vector>
9 #include "logline.h"
10 #include "memusage.h"   // Memusage2
11 #include "params.h"     // param_list_parse_*
12 #include "select_mpi.h"
13 #include "timing.h"     // seconds
14 #include "portability.h" // asprintf // IWYU pragma: keep
15 #include "misc.h"       // size_disp_fine
16 #include "macros.h"
17 
18 /* This is intended to provide progress info, and optionally also more
19  * detailed progress info when needed. Detailed progress info is assumed
20  * to possibly take several lines, while terse progress info is expected
21  * to take only one line.
22  */
23 
24 /* Log messages are provided in the form [[x, "text"]], with x an integer
25  * indicating a level of detail. 0 is the lowest level of detail, and
26  * higher means more detailed (and printed less often).
27  *
28  * Whenever we output an info which is more detailed than what we had
29  * before, and with no finishing newline, then the unfinished previous
30  * line is terminated with ..., and the terminating messaged will be
31  * issued alone later on.
32  *
33  * An exception is for level 0 (introduced by logline_begin()), for which
34  * the "title" is then echoed when finishing the printing.
35  */
36 struct logline {
37     /* Only log messages prefixed with <x> with x <= max will be printed
38      */
39     int max = 0;
40     /* Title string introducing this log set */
41     std::string header;
42     /* Did last print contain a trailing newline ? */
43     bool eol = false;
44     /* Last printed detail level */
45     int lastlevel = 0;
46 
47     FILE * f = NULL;
48 
49     /* Number of newlines seen (used to tell whether the title has to be
50      * echoed again) */
51     int nnl = 0;
52 
53     double start = 0;
54 
55     std::vector<std::string> prefixes;
56 };
57 
58 double logline_serialize();
59 void logline_unserialize(double);
60 
61 static struct logline * current;
62 
63 size_t logline_thresholds[10] = {
64     /* This list should be increasing */
65     SIZE_MAX,
66     SIZE_MAX,
67     SIZE_MAX,
68     SIZE_MAX,
69     SIZE_MAX,
70     SIZE_MAX,
71     SIZE_MAX,
72     SIZE_MAX,
73     SIZE_MAX,
74     SIZE_MAX,
75 };
76 
77 int logline_timings = 1;
78 
79 int logline_print_all_mpi_nodes = 0;
80 
81 static double start_time = -1;
82 
logline_init_timer()83 void logline_init_timer()
84 {
85     start_time = wct_seconds();
86 }
87 
88 int logline_report_wct = 0;
89 
logline_timer()90 static double logline_timer()
91 {
92     return logline_report_wct ? wct_seconds() : seconds();
93 }
94 
logline_serialize()95 double logline_serialize()
96 {
97     double tt = wct_seconds() - start_time;
98     return tt;
99 }
100 
logline_unserialize(double tt)101 void logline_unserialize(double tt)
102 {
103     start_time = wct_seconds() - tt;
104 }
105 
106 
logline_decl_usage(param_list_ptr pl)107 void logline_decl_usage(param_list_ptr pl)
108 {
109     param_list_decl_usage(pl, "logline_threshold",
110             "print log lines of verbosity level i only for sizes greater than i-th item in this comma-separated list");
111     param_list_decl_usage(pl, "logline_timings",
112             "print timings with log lines");
113     param_list_decl_usage(pl, "logline_report_wct",
114             "print wct taken by each step marked by log lines");
115     param_list_decl_usage(pl, "logline_print_all_mpi_nodes",
116             "enable logline printing on all MPI nodes");
117 
118 }
119 
logline_interpret_parameters(param_list_ptr pl)120 int logline_interpret_parameters(param_list_ptr pl)
121 {
122     int thr[10];
123     int n = param_list_parse_int_list(pl, "logline_threshold", thr, 10, ",");
124     for(int i = 0 ; i < n ; i++) {
125         logline_thresholds[i] = thr[i];
126     }
127     param_list_parse_int(pl, "logline_timings", &logline_timings);
128     param_list_parse_int(pl, "logline_report_wct", &logline_report_wct);
129     param_list_parse_int(pl, "logline_print_all_mpi_nodes", &logline_print_all_mpi_nodes);
130     return 0;
131 }
132 
logline_puts_raw(int level,const char * s)133 static void logline_puts_raw(int level, const char * s)
134 {
135     if (!current) return;
136     if (level > current->max) return;
137     if (level != current->lastlevel && !current->eol) {
138 
139         fputs(" ...\n", current->f);
140         current->nnl += (current->eol = 1);
141     }
142     for( ; current->prefixes.size() > (unsigned int) level ; current->prefixes.pop_back()) ;
143     if (current->eol) {
144         if (logline_timings) {
145             char buf1[16];
146             char buf2[16];
147             size_disp_fine(1024UL * Memusage2(), buf1, 10000.0);
148             size_disp_fine(1024UL * PeakMemusage(), buf2, 10000.0);
149             fprintf(current->f, "[%.3f %.3f %s %s] ", wct_seconds() - start_time, seconds(), buf1, buf2);
150         }
151         for(auto const & c : current->prefixes) {
152             fputs(c.c_str(), current->f);
153             fputc(' ', current->f);
154         }
155     } else if (level == current->lastlevel) {
156         fputc(' ', current->f);
157     }
158     fputs(s, current->f);
159     current->prefixes.push_back(s);
160     size_t n = strlen(s);
161     current->nnl += (current->eol = s[n-1] == '\n');
162     current->lastlevel = level;
163 }
164 
165 
logline_begin(FILE * f,size_t size,const char * fmt,...)166 int logline_begin(FILE * f, size_t size, const char * fmt, ...)
167 {
168     va_list ap;
169     int rank;
170     MPI_Comm_rank(MPI_COMM_WORLD, &rank);
171     if (rank && !logline_print_all_mpi_nodes) return 0;
172     ASSERT_ALWAYS(!current);
173     if (size < logline_thresholds[0]) return 0;
174     int level;
175     for(level = 0 ; level < 10 && size >= logline_thresholds[level] ; level++);
176     if (level == 0) return 0;
177     level--;
178     current = new logline;
179     current->max = level;
180     current->lastlevel = 0;
181     current->f = f;
182     current->start = logline_timer();
183     char * tmp;
184     va_start(ap, fmt);
185     int rc = vasprintf(&(tmp), fmt, ap);
186     ASSERT_ALWAYS(rc >= 0);
187     current->header = tmp;
188     free(tmp);
189     current->eol = true;
190     logline_puts_raw(0, current->header.c_str());
191     va_end(ap);
192     return 1;
193 }
194 
logline_end(double * rr,const char * fmt,...)195 int logline_end(double * rr, const char * fmt, ...)
196 {
197     if (!current) return 0;
198     va_list ap;
199     va_start(ap, fmt);
200     char * text2;
201     double tt = logline_timer() - current->start;
202     if (fmt) {
203         char * text;
204         int rc;
205         rc = vasprintf(&text, fmt, ap);
206         ASSERT_ALWAYS(rc >= 0);
207         rc = asprintf(&text2, "%s [%.2f]\n", text, tt);
208         ASSERT_ALWAYS(rc >= 0);
209         free(text);
210     } else {
211         int rc;
212         rc = asprintf(&text2, "[%.2f]\n", tt);
213         ASSERT_ALWAYS(rc >= 0);
214     }
215     if (current->nnl)
216         logline_puts_raw(0, current->header.c_str());
217     logline_puts_raw(0, text2);
218     free(text2);
219     delete current;
220     current = NULL;
221     va_end(ap);
222     if (rr) *rr += tt;
223     return 1;
224 }
225 
226 
logline_vprintf(int level,const char * fmt,va_list ap)227 int logline_vprintf(int level, const char * fmt, va_list ap)
228 {
229     if (!current) return 0;
230     char * text;
231     int rc = vasprintf(&text, fmt, ap);
232     ASSERT_ALWAYS(rc >= 0);
233     logline_puts_raw(level, text);
234     free(text);
235     return 1;
236 }
237 
logline_printf(int level,const char * fmt,...)238 int logline_printf(int level, const char * fmt, ...)
239 {
240     va_list ap;
241     va_start(ap, fmt);
242     logline_vprintf(level, fmt, ap);
243     va_end(ap);
244     return 1;
245 }
246 
247 
248