Merge branch 'main' of feistymeow.org:feisty_meow
[feisty_meow.git] / loggers / critical_events.cpp
1 /*****************************************************************************\
2 *                                                                             *
3 *  Name   : critical_events                                                   *
4 *  Author : Chris Koeritz                                                     *
5 *                                                                             *
6 *******************************************************************************
7 * Copyright (c) 1989-$now By Author.  This program is free software; you can  *
8 * redistribute it and/or modify it under the terms of the GNU General Public  *
9 * License as published by the Free Software Foundation; either version 2 of   *
10 * the License or (at your option) any later version.  This is online at:      *
11 *     http://www.fsf.org/copyleft/gpl.html                                    *
12 * Please send any updates to: fred@gruntose.com                               *
13 \*****************************************************************************/
14
15 #include "critical_events.h"
16 #include "program_wide_logger.h"
17
18 #include <application/application_shell.h>
19 #include <application/windoze_helper.h>
20 #include <basis/astring.h>
21 #include <basis/functions.h>
22 #include <basis/mutex.h>
23 #include <configuration/application_configuration.h>
24 #include <structures/static_memory_gremlin.h>
25 #include <textual/parser_bits.h>
26 #include <timely/time_stamp.h>
27
28 #include <stdio.h>
29 #ifndef _MSC_VER
30   #include <errno.h>
31 #endif
32
33 using namespace basis;
34 using namespace structures;
35 using namespace configuration;
36 using namespace textual;
37 using namespace timely;
38
39 const int MESSAGE_SPACE_PROVIDED = 4096;
40   // the strings should not be larger than this for diagnostic / error messages.
41
42 namespace loggers {
43
44 SAFE_STATIC(astring, critical_events::hidden_critical_events_dir, )
45   // define the function that holds the directory string.
46
47 astring default_critical_location()
48 {
49   // ensure that the critical events function logs to the appropriate place.
50   return application_configuration::get_logging_directory();
51 }
52
53 SAFE_STATIC(mutex, __critical_event_dir_lock, )
54
55 basis::un_int critical_events::system_error()
56 {
57 #if defined(__UNIX__) || defined(__GNU_WINDOWS__)
58   return errno;
59 #elif defined(_MSC_VER)
60   return GetLastError();
61 #else
62   #pragma error("hmmm: no code for error number for this operating system")
63   return 0;
64 #endif
65 }
66
67 astring critical_events::system_error_text(basis::un_int to_name)
68 {
69 #if defined(__UNIX__) || defined(__GNU_WINDOWS__)
70   return strerror(to_name);
71 #elif defined(_MSC_VER)
72   char error_text[1000];
73   FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL_POINTER, to_name,
74       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)error_text,
75       sizeof(error_text) - 1, NULL_POINTER);
76   astring to_return = error_text;
77   // trim off the ridiculous carriage return they add.
78   while ( (to_return[to_return.end()] == '\r')
79       || (to_return[to_return.end()] == '\n') )
80     to_return.zap(to_return.end(), to_return.end());
81   return to_return;
82 #else
83   #pragma error("hmmm: no code for error text for this operating system")
84   return "";
85 #endif
86 }
87
88 astring critical_events::critical_events_directory()
89 {
90   static bool initted = false;
91   if (!initted) {
92     auto_synchronizer l(__critical_event_dir_lock());
93     if (!initted) {
94       set_critical_events_directory(default_critical_location());
95       initted = true;
96     }
97   }
98   return hidden_critical_events_dir();
99 }
100
101 void critical_events::set_critical_events_directory(const astring &directory)
102 { hidden_critical_events_dir() = directory; }
103
104 void critical_events::write_to_critical_events(const char *to_write)
105 {
106   astring filename = critical_events_directory();
107   filename += "/runtime_issues.log";
108   FILE *errfile = fopen(filename.s(), "ab");
109   if (errfile) {
110     astring app_name = application_configuration::application_name();
111     int indy = app_name.find('/', app_name.end(), true);
112     if (non_negative(indy)) app_name.zap(0, indy);
113     indy = app_name.find('\\', app_name.end(), true);
114     if (non_negative(indy)) app_name.zap(0, indy);
115     fprintf(errfile, "%s [%s]:%s", time_stamp::notarize(true).s(),
116         app_name.s(), parser_bits::platform_eol_to_chars());
117     fprintf(errfile, "%s%s", to_write, parser_bits::platform_eol_to_chars());
118     fclose(errfile);
119   }
120 }
121
122 void critical_events::write_to_console(const char *guards_message_space)
123 { fprintf(stderr, "%s", (char *)guards_message_space); fflush(stderr); }
124
125 void critical_events::alert_message(const char *info, const char *title)
126 {
127   astring to_print;
128   if (strlen(title)) {
129     const char border = '=';
130     to_print += astring(border, int(strlen(title)) + 4);
131     to_print += parser_bits::platform_eol_to_chars();
132     to_print += border;
133     to_print += ' ';
134     to_print += title;
135     to_print += ' ';
136     to_print += border;
137     to_print += parser_bits::platform_eol_to_chars();
138     to_print += astring(border, int(strlen(title)) + 4);
139     to_print += parser_bits::platform_eol_to_chars();
140   }
141
142   to_print += info;
143   program_wide_logger::get().log(to_print, ALWAYS_PRINT);
144   fflush(NULL_POINTER);  // flush all output streams.
145 }
146
147 void critical_events::alert_message(const astring &info) { alert_message(info.s()); }
148
149 void critical_events::alert_message(const astring &info, const astring &title)
150 { alert_message(info.s(), title.s()); }
151
152 void critical_events::make_error_message(const char *file, int line,
153     const char *error_class, const char *error_function,
154     const char *info, char *guards_message_space)
155 {
156   strcpy(guards_message_space, "\nProblem reported for \"");
157 //hmmm: only copy N chars of each into the space.
158 //      say 40 for class/function each, then the space - consumed for the
159 //      info.  get strlen for real on the class and function name to know
160 //      actual size for that computation.
161   strcat(guards_message_space, error_class);
162   strcat(guards_message_space, "::");
163   strcat(guards_message_space, error_function);
164   strcat(guards_message_space, "\"\n(invoked at line ");
165   char line_num[20];
166   sprintf(line_num, "%d", line);
167   strcat(guards_message_space, line_num);
168   strcat(guards_message_space, " in ");
169   strcat(guards_message_space, file);
170   strcat(guards_message_space, " at ");
171   strcat(guards_message_space, time_stamp::notarize(false).s());
172   strcat(guards_message_space, ")\n");
173   strcat(guards_message_space, info);
174   strcat(guards_message_space, "\n\n\n");
175 }
176
177 void critical_events::FL_deadly_error(const char *file, int line, const char *error_class,
178     const char *error_function, const char *info)
179 {
180   FL_continuable_error(file, line, error_class, error_function, info,
181       "Deadly Error Information");
182   CAUSE_BREAKPOINT;
183   throw "deadly_error";
184     // abort() is not as good an approach as throwing an exception.  aborts are
185     // harder to track with some compilers, but all should be able to trap an
186     // exception.
187 }
188
189 void critical_events::FL_deadly_error(const astring &file, int line,
190     const astring &error_class, const astring &error_function,
191     const astring &info)
192 {
193   FL_deadly_error(file.s(), line, error_class.s(), error_function.s(),
194       info.s()); 
195 }
196
197 void critical_events::FL_continuable_error_real(const char *file, int line,
198     const char *error_class, const char *error_function, const char *info,
199     const char *title)
200 {
201   char guards_message_space[MESSAGE_SPACE_PROVIDED];
202     // this routine could still fail, if the call stack is already jammed
203     // against its barrier.  but if that's the case, even the simple function
204     // call might fail.  in any case, we cannot deal with a stack overflow
205     // type error using this function.  but we would rather deal with that
206     // than make the space static since that would cause corruption when
207     // two threads wrote errors at the same time.
208   make_error_message(file, line, error_class, error_function, info,
209       guards_message_space);
210   alert_message(guards_message_space, title);
211 }
212
213 void critical_events::FL_continuable_error(const char *file, int line,
214     const char *error_class, const char *error_function, const char *info,
215     const char *title)
216 {
217   FL_continuable_error_real(file, line, error_class, error_function, info, title);
218 }
219
220 void critical_events::FL_continuable_error(const astring &file, int line,
221     const astring &error_class, const astring &error_function,
222     const astring &info, const astring &title)
223 {
224   FL_continuable_error_real(file.s(), line, error_class.s(),
225       error_function.s(), info.s(), title.s());
226 }
227
228 void critical_events::FL_non_continuable_error(const char *file, int line,
229     const char *error_class, const char *error_function, const char *info,
230     const char *title)
231 {
232   FL_continuable_error_real(file, line, error_class, error_function, info,
233       title);
234   exit(EXIT_FAILURE);  // let the outside world know that there was a problem.
235 }
236
237 void critical_events::FL_non_continuable_error(const astring &file, int line,
238     const astring &error_class, const astring &error_function,
239     const astring &info, const astring &title)
240 {
241   FL_continuable_error_real(file.s(), line, error_class.s(),
242       error_function.s(), info.s(), title.s());
243   exit(EXIT_FAILURE);  // let the outside world know that there was a problem.
244 }
245
246 void critical_events::FL_console_error(const char *file, int line, const char *error_class,
247     const char *error_function, const char *info)
248 {
249   char guards_message_space[MESSAGE_SPACE_PROVIDED];
250   make_error_message(file, line, error_class, error_function, info,
251       guards_message_space);
252   write_to_console(guards_message_space);
253 }
254
255 void critical_events::FL_out_of_memory_now(const char *file, int line,
256     const char *the_class_name, const char *the_func)
257 {
258   FL_non_continuable_error(file, line, the_class_name, the_func, 
259       "Program stopped due to memory allocation failure.",
260       "Out of Memory Now");
261 }
262
263 void critical_events::implement_bounds_halt(const char *the_class_name, const char *the_func,
264     const char *value, const char *low, const char *high,
265     const char *error_addition)
266 {
267   char message[400];
268   strcpy(message, "bounds error caught");
269   strcat(message, error_addition);
270   strcat(message, ":\r\n");
271   strcat(message, value);
272   strcat(message, " is not between ");
273   strcat(message, low);
274   strcat(message, " and ");
275   strcat(message, high);
276 #ifdef ERRORS_ARE_FATAL
277   deadly_error(the_class_name, the_func, message);
278 #else
279   continuable_error(the_class_name, the_func, message);
280 #endif
281 }
282
283 } //namespace.
284