source: branches/stable/CORE/arb_msg.cxx

Last change on this file was 12303, checked in by westram, 5 years ago
File size: 16.4 KB
Line 
1// ================================================================ //
2//                                                                  //
3//   File      : arb_msg.cxx                                        //
4//   Purpose   :                                                    //
5//                                                                  //
6//   Coded by Ralf Westram (coder@reallysoft.de) in November 2010   //
7//   Institute of Microbiology (Technical University Munich)        //
8//   http://www.arb-home.de/                                        //
9//                                                                  //
10// ================================================================ //
11
12#include <arb_msg_fwd.h>
13#include <arb_assert.h>
14#include <arb_string.h>
15#include <arb_backtrace.h>
16#include <smartptr.h>
17#include <arb_handlers.h>
18
19// AISC_MKPT_PROMOTE:#ifndef _GLIBCXX_CSTDLIB
20// AISC_MKPT_PROMOTE:#include <cstdlib>
21// AISC_MKPT_PROMOTE:#endif
22// AISC_MKPT_PROMOTE:#ifndef ARB_CORE_H
23// AISC_MKPT_PROMOTE:#include "arb_core.h"
24// AISC_MKPT_PROMOTE:#endif
25// AISC_MKPT_PROMOTE:
26// AISC_MKPT_PROMOTE:// return error and ensure none is exported
27// AISC_MKPT_PROMOTE:#define RETURN_ERROR(err)  arb_assert(!GB_have_error()); return (err)
28// AISC_MKPT_PROMOTE:
29
30#if defined(DEBUG)
31#if defined(DEVEL_RALF)
32// #define TRACE_BUFFER_USAGE
33#endif // DEBUG
34#endif // DEVEL_RALF
35
36#define GLOBAL_STRING_BUFFERS 4
37
38static size_t last_global_string_size = 0;
39#define GBS_GLOBAL_STRING_SIZE 64000
40
41// --------------------------------------------------------------------------------
42
43#ifdef LINUX
44# define HAVE_VSNPRINTF
45#endif
46
47#ifdef HAVE_VSNPRINTF
48# define PRINT2BUFFER(buffer, bufsize, templat, parg) vsnprintf(buffer, bufsize, templat, parg)
49#else
50# define PRINT2BUFFER(buffer, bufsize, templat, parg) vsprintf(buffer, templat, parg)
51#endif
52
53#define PRINT2BUFFER_CHECKED(printed, buffer, bufsize, templat, parg)   \
54    (printed) = PRINT2BUFFER(buffer, bufsize, templat, parg);           \
55    if ((printed) < 0 || (size_t)(printed) >= (bufsize)) {              \
56        GBK_terminatef("Internal buffer overflow (size=%zu, used=%i)\n", \
57                       (bufsize), (printed));                           \
58    }
59
60// --------------------------------------------------------------------------------
61
62__ATTR__VFORMAT(1) static const char *gbs_vglobal_string(const char *templat, va_list parg, int allow_reuse) {
63    static char buffer[GLOBAL_STRING_BUFFERS][GBS_GLOBAL_STRING_SIZE+2]; // several buffers - used alternately
64    static int  idx                             = 0;
65    static char lifetime[GLOBAL_STRING_BUFFERS] = {};
66    static char nextIdx[GLOBAL_STRING_BUFFERS] = {};
67
68    int my_idx;
69    int psize;
70
71    if (nextIdx[0] == 0) { // initialize nextIdx
72        for (my_idx = 0; my_idx<GLOBAL_STRING_BUFFERS; my_idx++) {
73            nextIdx[my_idx] = (my_idx+1)%GLOBAL_STRING_BUFFERS;
74        }
75    }
76
77    if (allow_reuse == -1) { // called from GBS_reuse_buffer
78        // buffer to reuse is passed in 'templat'
79
80        for (my_idx = 0; my_idx<GLOBAL_STRING_BUFFERS; my_idx++) {
81            if (buffer[my_idx] == templat) {
82                lifetime[my_idx] = 0;
83#if defined(TRACE_BUFFER_USAGE)
84                printf("Reusing buffer #%i\n", my_idx);
85#endif // TRACE_BUFFER_USAGE
86                if (nextIdx[my_idx] == idx) idx = my_idx;
87                return 0;
88            }
89#if defined(TRACE_BUFFER_USAGE)
90            else {
91                printf("(buffer to reuse is not buffer #%i (%p))\n", my_idx, buffer[my_idx]);
92            }
93#endif // TRACE_BUFFER_USAGE
94        }
95        for (my_idx = 0; my_idx<GLOBAL_STRING_BUFFERS; my_idx++) {
96            printf("buffer[%i]=%p\n", my_idx, buffer[my_idx]);
97        }
98        arb_assert(0);       // GBS_reuse_buffer called with illegal buffer
99        return 0;
100    }
101
102    if (lifetime[idx] == 0) {
103        my_idx = idx;
104    }
105    else {
106        for (my_idx = nextIdx[idx]; lifetime[my_idx]>0; my_idx = nextIdx[my_idx]) {
107#if defined(TRACE_BUFFER_USAGE)
108            printf("decreasing lifetime[%i] (%i->%i)\n", my_idx, lifetime[my_idx], lifetime[my_idx]-1);
109#endif // TRACE_BUFFER_USAGE
110            lifetime[my_idx]--;
111        }
112    }
113
114    PRINT2BUFFER_CHECKED(psize, buffer[my_idx], (size_t)GBS_GLOBAL_STRING_SIZE, templat, parg);
115
116#if defined(TRACE_BUFFER_USAGE)
117    printf("Printed into global buffer #%i ('%s')\n", my_idx, buffer[my_idx]);
118#endif // TRACE_BUFFER_USAGE
119
120    last_global_string_size = psize;
121
122    if (!allow_reuse) {
123        idx           = my_idx;
124        lifetime[idx] = 1;
125    }
126#if defined(TRACE_BUFFER_USAGE)
127    else {
128        printf("Allow reuse of buffer #%i\n", my_idx);
129    }
130#endif // TRACE_BUFFER_USAGE
131
132    return buffer[my_idx];
133}
134
135const char *GBS_vglobal_string(const char *templat, va_list parg) {
136    // goes to header: __ATTR__VFORMAT(1)
137    return gbs_vglobal_string(templat, parg, 0);
138}
139
140char *GBS_vglobal_string_copy(const char *templat, va_list parg) {
141    // goes to header: __ATTR__VFORMAT(1)
142    const char *gstr = gbs_vglobal_string(templat, parg, 1);
143    return GB_strduplen(gstr, last_global_string_size);
144}
145
146const char *GBS_global_string_to_buffer(char *buffer, size_t bufsize, const char *templat, ...) {
147    // goes to header: __ATTR__FORMAT(3)
148
149#if defined(WARN_TODO)
150#warning search for '\b(sprintf)\b\s*\(' and replace by GBS_global_string_to_buffer
151#endif
152
153    va_list parg;
154    int     psize;
155
156    arb_assert(buffer);
157    va_start(parg, templat);
158    PRINT2BUFFER_CHECKED(psize, buffer, bufsize, templat, parg);
159    va_end(parg);
160
161    return buffer;
162}
163
164char *GBS_global_string_copy(const char *templat, ...) {
165    // goes to header: __ATTR__FORMAT(1)
166    va_list parg;
167    va_start(parg, templat);
168    char *result = GBS_vglobal_string_copy(templat, parg);
169    va_end(parg);
170    return result;
171}
172
173const char *GBS_global_string(const char *templat, ...) {
174    // goes to header: __ATTR__FORMAT(1)
175    va_list parg;
176    va_start(parg, templat);
177    const char *result = gbs_vglobal_string(templat, parg, 0);
178    va_end(parg);
179    return result;
180}
181
182const char *GBS_static_string(const char *str) {
183    return GBS_global_string("%s", str);
184}
185
186GB_ERROR GBK_assert_msg(const char *assertion, const char *file, int linenr) {
187#define BUFSIZE 1000
188    static char *buffer   = 0;
189    const char  *result   = 0;
190    int          old_size = last_global_string_size;
191
192    if (!buffer) buffer = (char *)malloc(BUFSIZE);
193    result              = GBS_global_string_to_buffer(buffer, BUFSIZE, "assertion '%s' failed in %s #%i", assertion, file, linenr);
194
195    last_global_string_size = old_size;
196
197    return result;
198#undef BUFSIZE
199}
200
201// -------------------------
202//      Error "handling"
203
204
205#if defined(WARN_TODO)
206#warning redesign GB_export_error et al
207/* To clearly distinguish between the two ways of error handling
208 * (which are: return GB_ERROR
209 *  and:       export the error)
210 *
211 * GB_export_error() shall only export, not return the error message.
212 * if only used for formatting GBS_global_string shall be used
213 * (most cases where GB_export_errorf is used are candidates for this.
214 *  GB_export_error was generally misused for this, before
215 *  GBS_global_string was added!)
216 *
217 * GB_export_IO_error() shall not export and be renamed into GB_IO_error()
218 *
219 * GB_export_error() shall fail if there is already an exported error
220 * (maybe always remember a stack trace of last error export (try whether copy of backtrace-array works))
221 *
222 * use GB_get_error() to import AND clear the error
223 */
224#endif
225
226static char *GB_error_buffer = 0;
227
228GB_ERROR GB_export_error(const char *error) { // just a temp hack around format-warnings
229    return GB_export_errorf("%s", error);
230}
231
232GB_ERROR GB_export_errorf(const char *templat, ...) {
233    /* goes to header:
234     * __ATTR__FORMAT(1)
235     * __ATTR__DEPRECATED_LATER("use GB_export_error(GBS_global_string(...))")
236     *          because it's misused (where GBS_global_string should be used)
237     *          old functionality will remain available via 'GB_export_error(GBS_global_string(...))'
238     */
239
240    char     buffer[GBS_GLOBAL_STRING_SIZE];
241    char    *p = buffer;
242    va_list  parg;
243
244#if defined(WARN_TODO)
245#warning dont prepend ARB ERROR here
246#endif
247
248    p += sprintf(buffer, "ARB ERROR: ");
249    va_start(parg, templat);
250
251    vsprintf(p, templat, parg);
252
253    freedup(GB_error_buffer, buffer);
254    return GB_error_buffer;
255}
256
257#if defined(WARN_TODO)
258#warning replace GB_export_IO_error() by GB_IO_error() and then export it if really needed
259#endif
260
261GB_ERROR GB_IO_error(const char *action, const char *filename) {
262    /*! creates error message from current 'errno'
263     * @param action may be NULL (otherwise it should contain sth like "writing" or "deleting")
264     * @param filename may be NULL (otherwise it should contain the filename, the IO-Error occurred for)
265     * @return error message (in static buffer)
266     */
267
268    GB_ERROR io_error;
269    if (errno) {
270        io_error = strerror(errno);
271    }
272    else {
273        arb_assert(0);           // unhandled error (which is NOT an IO-Error)
274        io_error =
275            "Some unhandled error occurred, but it was not an IO-Error. "
276            "Please send detailed information about how the error occurred to devel@arb-home.de "
277            "or ignore this error (if possible).";
278    }
279
280    GB_ERROR error;
281    if (action) {
282        if (filename) error = GBS_global_string("While %s '%s': %s", action, filename, io_error);
283        else error          = GBS_global_string("While %s <unknown file>: %s", action, io_error);
284    }
285    else {
286        if (filename) error = GBS_global_string("Concerning '%s': %s", filename, io_error);
287        else error          = io_error; 
288    }
289
290    return error;
291}
292
293GB_ERROR GB_export_IO_error(const char *action, const char *filename) {
294    // goes to header: __ATTR__DEPRECATED_TODO("use GB_export_error(GB_IO_error(...))")
295    return GB_export_error(GB_IO_error(action, filename));
296}
297
298#if defined(WARN_TODO)
299#warning reactivate deprecations below
300#endif
301
302
303GB_ERROR GB_print_error() {
304    // goes to header: __ATTR__DEPRECATED_TODO("will be removed completely")
305    if (GB_error_buffer) {
306        fflush(stdout);
307        fprintf(stderr, "%s\n", GB_error_buffer);
308    }
309    return GB_error_buffer;
310}
311
312GB_ERROR GB_get_error() {
313    // goes to header: __ATTR__DEPRECATED_TODO("consider using either GB_have_error() or GB_await_error()")
314    return GB_error_buffer;
315}
316
317bool GB_have_error() {
318    return GB_error_buffer != 0;
319}
320
321GB_ERROR GB_await_error() {
322    if (GB_error_buffer) {
323        static SmartCharPtr err;
324        err             = GB_error_buffer;
325        GB_error_buffer = NULL;
326        return &*err;
327    }
328    arb_assert(0);               // please correct error handling
329
330    return "Program logic error: Something went wrong, but reason is unknown";
331}
332
333void GB_clear_error() {         // clears the error buffer
334    freenull(GB_error_buffer);
335}
336
337#if defined(WARN_TODO)
338#warning search for 'GBS_global_string.*error' and replace with GB_failedTo_error or GB_append_exportedError
339#endif
340GB_ERROR GB_failedTo_error(const char *do_something, const char *special, GB_ERROR error) {
341    if (error) {
342        if (special) {
343            error = GBS_global_string("Failed to %s '%s'.\n(Reason: %s)", do_something, special, error);
344        }
345        else {
346            error = GBS_global_string("Failed to %s.\n(Reason: %s)", do_something, error);
347        }
348    }
349    return error;
350}
351
352GB_ERROR GB_append_exportedError(GB_ERROR error) {
353    // If an error has been exported, it gets appended as reason to given 'error'.
354    // If error is NULL, the exported error is returned (if any)
355    //
356    // This is e.g. useful if you search for SOMETHING in the DB w/o success (i.e. get NULL as result).
357    // In that case you can't be sure, whether SOMETHING just does not exist or whether it was not
358    // found because some other error occurred.
359
360    if (GB_have_error()) {
361        if (error) return GBS_global_string("%s (Reason: %s)", error, GB_await_error());
362        return GB_await_error();
363    }
364    return error;
365}
366
367// ---------------------
368//      Backtracing
369
370class BackTraceInfo *GBK_get_backtrace(size_t skipFramesAtBottom) { // only used ifdef TRACE_ALLOCS
371    return new BackTraceInfo(skipFramesAtBottom);
372}
373void GBK_dump_former_backtrace(class BackTraceInfo *trace, FILE *out, const char *message) { // only used ifdef TRACE_ALLOCS
374    demangle_backtrace(*trace, out, message);
375}
376
377void GBK_free_backtrace(class BackTraceInfo *trace) { // only used ifdef TRACE_ALLOCS
378    delete trace;
379}
380
381void GBK_dump_backtrace(FILE *out, const char *message) {
382    demangle_backtrace(BackTraceInfo(1), out ? out : stderr, message);
383}
384
385// -------------------------------------------
386//      Error/notification functions
387
388void GB_internal_error(const char *message) {
389    /* Use GB_internal_error, when something goes badly wrong
390     * but you want to give the user a chance to save his database
391     *
392     * Note: it's NOT recommended to use this function!
393     */
394
395    char *full_message = GBS_global_string_copy("Internal ARB Error: %s", message);
396    active_arb_handlers->show_error(full_message);
397    active_arb_handlers->show_error("ARB is most likely unstable now (due to this error).\n"
398                                    "If you've made changes to the database, consider to save it using a different name.\n"
399                                    "Try to fix the cause of the error and restart ARB.");
400
401#ifdef ASSERTION_USED
402    fputs(full_message, stderr);
403    arb_assert(0);               // internal errors shall not happen, go fix it
404#else
405    GBK_dump_backtrace(stderr, full_message);
406#endif
407
408    free(full_message);
409}
410
411void GB_internal_errorf(const char *templat, ...) {
412    // goes to header: __ATTR__FORMAT(1)
413    FORWARD_FORMATTED_NORETURN(GB_internal_error, templat);
414}
415
416void GBK_terminate(const char *error) { // goes to header __ATTR__NORETURN
417    /* GBK_terminate is the emergency exit!
418     * only used if no other way to recover
419     */
420
421    fprintf(stderr, "Error: '%s'\n", error);
422    fputs("Can't continue - terminating..\n", stderr);
423    GBK_dump_backtrace(stderr, "GBK_terminate (reason above) ");
424
425    fflush(stderr);
426    ARB_SIGSEGV(0); // GBK_terminate shall not be called, fix reason for call (this will crash in RELEASE version)
427    exit(ARB_CRASH_CODE(0)); // should not be reached..just ensure ARB really terminates if somebody changes ARB_SIGSEGV
428}
429
430void GBK_terminatef(const char *templat, ...) {
431    // goes to header: __ATTR__FORMAT(1) __ATTR__NORETURN
432    FORWARD_FORMATTED_NORETURN(GBK_terminate, templat);
433}
434
435// AISC_MKPT_PROMOTE:inline void GBK_terminate_on_error(const char *error) { if (error) GBK_terminatef("Fatal error: %s", error); }
436
437void GB_warning(const char *message) {
438    /* If program uses GUI, the message is printed via aw_message, otherwise it goes to stdout
439     * see also : GB_information
440     */
441    active_arb_handlers->show_warning(message);
442}
443void GB_warningf(const char *templat, ...) {
444    // goes to header: __ATTR__FORMAT(1)
445    FORWARD_FORMATTED(GB_warning, templat);
446}
447
448void GB_information(const char *message) {
449    /* this message is always printed to stdout (regardless whether program uses GUI or not)
450     * (if it is not redirected using ARB_redirect_handlers_to)
451     * see also : GB_warning
452     */
453    active_arb_handlers->show_message(message);
454}
455void GB_informationf(const char *templat, ...) {
456    // goes to header: __ATTR__FORMAT(1)
457    FORWARD_FORMATTED(GB_information, templat);
458}
459
460
461#pragma GCC diagnostic ignored "-Wmissing-format-attribute"
462
463void GBS_reuse_buffer(const char *global_buffer) {
464    // If you've just shortely used a buffer, you can put it back here
465    va_list empty;
466    gbs_vglobal_string(global_buffer, empty, -1); // omg hax
467}
468
469#if defined(WARN_TODO)
470#warning search for '\b(system)\b\s*\(' and use GBK_system instead
471#endif
472GB_ERROR GBK_system(const char *system_command) {
473    // goes to header: __ATTR__USERESULT
474    fflush(stdout);
475    fprintf(stderr, "[Action: '%s']\n", system_command); fflush(stderr);
476
477    int res = system(system_command);
478
479    fflush(stdout);
480    fflush(stderr);
481
482    GB_ERROR error = NULL;
483    if (res) {
484        if (res == -1) {
485            error = GB_IO_error("forking", system_command);
486            error = GBS_global_string("System call failed (Reason: %s)", error);
487        }
488        else {
489            error = GBS_global_string("System call failed (result=%i)", res);
490        }
491
492        error = GBS_global_string("%s\n"
493                                  "System call was '%s'%s",
494                                  error, system_command,
495                                  res == -1 ? "" : "\n(Note: console window may contain additional information)");
496    }
497    return error;
498}
499
Note: See TracBrowser for help on using the repository browser.