1 /* melder_error.cpp
2 *
3 * Copyright (C) 1992-2021 Paul Boersma
4 *
5 * This code is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or (at
8 * your option) any later version.
9 *
10 * This code is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this work. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "melder.h"
20
defaultErrorProc(conststring32 message)21 static void defaultErrorProc (conststring32 message) {
22 MelderConsole::write (str32str (message, U"You interrupted ") ? U"User interrupt: " : U"Error: ", true);
23 MelderConsole::write (message, true);
24 MelderConsole::write (U"\n", true);
25 }
defaultCrashProc(conststring32 message)26 static void defaultCrashProc (conststring32 message) {
27 MelderConsole::write (U"Crashing bug: ", true);
28 MelderConsole::write (message, true);
29 MelderConsole::write (U"\n", true);
30 }
31
32 static void (*p_theErrorProc) (conststring32 message) = & defaultErrorProc; // initial setting after start-up; will stay if program is run from batch
33 static void (*p_theCrashProc) (conststring32 message) = & defaultCrashProc; // initial setting after start-up; will stay if program is run from batch
34
Melder_setErrorProc(void (* p_errorProc)(conststring32))35 void Melder_setErrorProc (void (*p_errorProc) (conststring32)) {
36 p_theErrorProc = p_errorProc ? p_errorProc : & defaultErrorProc;
37 }
Melder_setCrashProc(void (* p_crashProc)(conststring32))38 void Melder_setCrashProc (void (*p_crashProc) (conststring32)) {
39 p_theCrashProc = p_crashProc ? p_crashProc : & defaultCrashProc;
40 }
41
42 constexpr integer BUFFER_LENGTH = 2000;
43 static char32 theErrorBuffer [BUFFER_LENGTH]; // safe in low-memory situations
44
_append(conststring32 message)45 void MelderError::_append (conststring32 message) {
46 if (! message)
47 return;
48 integer length = str32len (theErrorBuffer), messageLength = str32len (message);
49 if (length + messageLength >= BUFFER_LENGTH)
50 return;
51 str32cpy (theErrorBuffer + length, message);
52 }
53
Melder_hasError()54 bool Melder_hasError () {
55 return theErrorBuffer [0] != U'\0';
56 }
57
Melder_hasError(conststring32 partialError)58 bool Melder_hasError (conststring32 partialError) {
59 return !! str32str (theErrorBuffer, partialError);
60 }
61
62 #define CRASH_SEMAPHORE U" will crash. Please notify the author "
63
Melder_hasCrash()64 bool Melder_hasCrash () {
65 const char32 *firstSpace = str32chr (theErrorBuffer, U' ');
66 if (! firstSpace)
67 return false;
68 return Melder_stringMatchesCriterion (firstSpace, kMelder_string::STARTS_WITH, CRASH_SEMAPHORE, false);
69 }
70
Melder_clearError()71 void Melder_clearError () {
72 if (Melder_hasCrash ())
73 throw MelderError ();
74 theErrorBuffer [0] = U'\0';
75 }
76
Melder_getError()77 conststring32 Melder_getError () {
78 return & theErrorBuffer [0];
79 }
80
Melder_appendError_noLine(const MelderArg & arg)81 void Melder_appendError_noLine (const MelderArg& arg) {
82 MelderError::_append (arg._arg);
83 }
84
Melder_flushError()85 void Melder_flushError () {
86 if (Melder_hasCrash ()) {
87 (*p_theCrashProc) (theErrorBuffer);
88 abort ();
89 } else {
90 /*
91 "errors" has to be cleared *before* the message is put on the screen.
92 This is because on some platforms the message dialog is synchronous
93 (Melder_flushError will wait until the message dialog is closed),
94 and some operating systems may force an immediate redraw event as soon as
95 the message dialog is closed. We want "errors" to be empty when redrawing!
96 */
97 static char32 temp [BUFFER_LENGTH];
98 str32cpy (temp, theErrorBuffer);
99 theErrorBuffer [0] = U'\0';
100 (*p_theErrorProc) (temp);
101 }
102 }
103
104 #include <mutex>
105 static std::mutex theMelder_crash_mutex; // to guard against simultaneous crashes in multiple threads
106
107 static const conststring32 theCrashMessage { U"Praat" CRASH_SEMAPHORE // TODO: make dependent on app name
108 "(paul.boersma@uva.nl) with all of the following information, "
109 "before closing this window (and please mention in your email precisely what you were doing when this crash occurred):\n\n" };
110
Melder_fatal_(const MelderArg & arg1,const MelderArg & arg2,const MelderArg & arg3,const MelderArg & arg4,const MelderArg & arg5,const MelderArg & arg6,const MelderArg & arg7,const MelderArg & arg8,const MelderArg & arg9,const MelderArg & arg10,const MelderArg & arg11,const MelderArg & arg12,const MelderArg & arg13)111 void Melder_fatal_ (const MelderArg& arg1,
112 const MelderArg& arg2, const MelderArg& arg3, const MelderArg& arg4,
113 const MelderArg& arg5, const MelderArg& arg6, const MelderArg& arg7,
114 const MelderArg& arg8, const MelderArg& arg9, const MelderArg& arg10,
115 const MelderArg& arg11, const MelderArg& arg12, const MelderArg& arg13)
116 {
117 std::lock_guard <std::mutex> lock (theMelder_crash_mutex);
118 MelderError::_append (theCrashMessage);
119 MelderError::_append (arg1. _arg ? arg1. _arg : U"");
120 MelderError::_append (arg2. _arg ? arg2. _arg : U"");
121 MelderError::_append (arg3. _arg ? arg3. _arg : U"");
122 MelderError::_append (arg4. _arg ? arg4. _arg : U"");
123 MelderError::_append (arg5. _arg ? arg5. _arg : U"");
124 MelderError::_append (arg6. _arg ? arg6. _arg : U"");
125 MelderError::_append (arg7. _arg ? arg7. _arg : U"");
126 MelderError::_append (arg8. _arg ? arg8. _arg : U"");
127 MelderError::_append (arg9. _arg ? arg9. _arg : U"");
128 MelderError::_append (arg10._arg ? arg10._arg : U"");
129 MelderError::_append (arg11._arg ? arg11._arg : U"");
130 MelderError::_append (arg12._arg ? arg12._arg : U"");
131 MelderError::_append (arg13._arg ? arg13._arg : U"");
132 trace (U"CRASH: ", theErrorBuffer);
133 throw MelderError ();
134 }
135
Melder_assert_(const char * pathName,int lineNumber,const char * condition)136 void Melder_assert_ (const char *pathName, int lineNumber, const char *condition) {
137 /*
138 This function tries to make sure that it allocates no heap memory.
139 Hence, character conversion is done in place rather than with Melder_peek8to32(),
140 and Melder_integer() is also avoided.
141 */
142 std::lock_guard <std::mutex> lock (theMelder_crash_mutex); // to guard against simultaneous crashes in multiple threads
143 static char32 pathNameBuffer [1000], conditionBuffer [1000], lineNumberBuffer [40];
144 Melder_8to32_inplace (pathName, pathNameBuffer, kMelder_textInputEncoding::UTF8);
145 const char32 *p_lastFolderSeparator = str32rchr (pathNameBuffer, U'/');
146 if (! p_lastFolderSeparator)
147 p_lastFolderSeparator = str32rchr (pathNameBuffer, U'\\');
148 const conststring32 fileName = ( p_lastFolderSeparator ? p_lastFolderSeparator + 1 : pathNameBuffer );
149 Melder_8to32_inplace (condition, conditionBuffer, kMelder_textInputEncoding::UTF8);
150 static char lineNumberBuffer8 [40];
151 sprintf (lineNumberBuffer8, "%d", lineNumber);
152 Melder_8to32_inplace (lineNumberBuffer8, lineNumberBuffer, kMelder_textInputEncoding::UTF8);
153 MelderError::_append (theCrashMessage);
154 MelderError::_append (U"Assertion failed in file \"");
155 MelderError::_append (fileName);
156 MelderError::_append (U"\" at line ");
157 MelderError::_append (lineNumberBuffer);
158 MelderError::_append (U":\n ");
159 MelderError::_append (conditionBuffer);
160 MelderError::_append (U"\n\n");
161 trace (U"CRASH: ", theErrorBuffer);
162 throw MelderError ();
163 }
164
165 /* End of file melder_error.cpp */
166