1 /* melder_debug.cpp
2  *
3  * Copyright (C) 2000-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 #ifdef linux
21 	#include "../sys/GuiP.h"
22 #endif
23 #include <time.h>
24 #include "../sys/praat_version.h"
25 #ifdef _WIN32
26 	#include "../kar/UnicodeData.h"
27 	#include <windows.h>
28 #endif
29 
30 /*
31 Melder_debug will always be set to 0 when Praat starts up.
32 If Melder_debug is temporarily set to the following values
33 (preferably with the "Debug..." command under Praat->Technical,
34  which you can use from a script),
35 the behaviour of Praat will temporarily change in the following ways:
36 
37 1: Windows: use C-clock instead of multimedia-clock in melder_audio.cpp.
38 2: Windows: always reset waveOut, even when played to end, in melder_audio.cpp.
39 3: Windows: reset waveOut if unprepareHeader fails, and retry, in melder_audio.cpp. STAY: 20010214
40 4: Windows: fewer callbacks during sound play, in melder_audio.cpp. STAY: 20010214
41 6: Windows: info on metafile properties in Picture.cpp.
42 8: Windows: don't reset waveIn, in SoundRecorder.cpp.
43 9: flush Error in FunctionEditor_Sound_draw
44 10: geometric pens
45 11: clicked item in option menu in Ui.cpp
46 14: switches off the progress window in melder.cpp
47 15: don't use TrueType IPA fonts, but always bitmaps instead
48 16: Linux: open /dev/dsp without O_NDELAY
49 17: debugging on in Formula.cpp
50 18: no endian assumptions in abcio.cpp
51 19: show path name in UiOutfile_do
52 20: trace PortAudio
53 21: Mac: list supported document formats when printing
54 22: UTF-8 tests in logo
55 23: recognize special chunks in WAV files
56 24: measure buttons in drawing area
57 25: read crooked Manipulation files (January 2008)
58 26: force OT-GLA
59 27: force HG-GLA
60 28: don't use GSL in NUMfisherQ
61 29: use GSL in NUMinvFisherQ
62 30: pitch path finder: use octave jump cost across voiceless parts
63 31: Pitch analysis: formant pulling on
64 32: show info on file names in ExperimentMFC
65 33: trace the Pitch path finder
66 34: trace memory allocation and deallocation
67 35: debugging on for QuickTime movie file opening
68 37: debug autoThing
69 39: debug autostring
70 40: debug Thing_new
71 41: OTGrammar_getWinner: always first choice rather than random choice
72 42: OTGrammar_getWinner: always last choice rather than random choice
73 43: trace class table initialization
74 44: trace Collection
75 45: tracing structMatrix :: read ()
76 46: trace GTK parent sizes in _GuiObject_position ()
77 47: force resampling in OTGrammar RIP
78 48: compute sum, mean, stdev with naive implementation in double (64 bits)
79 49: compute sum, mean, stdev with naive implementation in longdouble (80 bits)
80 50: compute sum, mean, stdev with first-element offset (80 bits)
81 51: compute sum, mean, stdev with two cycles, as in R (80 bits)
82 (other numbers than 48-51: compute sum, mean, stdev with simple pairwise algorithm, base case 64 [80 bits])
83 52: debug Discriminant_TableOfReal_to_ClassificationTable
84 53: trace running cursor
85 54: ignore gdk_cairo_reset_clip
86 55: trace Gui init, draw, destroy
87 181: read and write native-endian real64
88 900: use DG Meta Serif Science instead of Palatino
89 1264: Mac: Sound_record_fixedTime uses microphone "FW Solo (1264)"
90 
91 (negative values are for David)
92 
93 */
94 
95 /*
96  * In order to make sure that Melder_casual() and trace() can be called from anywhere,
97  * including e.g. from Melder_realloc() or Melder_free(),
98  * they cannot use any Melder_xxx() functions.
99  */
100 
101 /*
102  * peek32to8 substitutes for Melder_peek32to8(),
103  * which can call Melder_realloc() and Melder_free();
104  * also, we need no newline nativization, as Melder_32to8_inplace() does.
105  */
_peek32to8(conststring32 string)106 conststring8 MelderTrace::_peek32to8 (conststring32 string) {
107 	if (! string) return "";
108 	static char *buffer { nullptr };
109 	static int64 bufferSize { 0 };
110 	int64 n = str32len (string);
111 	int64 sizeNeeded = n * 4 + 1;
112 	if ((bufferSize - sizeNeeded) * (int64) sizeof (char) >= 10'000) {
113 		free (buffer);
114 		buffer = nullptr;   // undangle
115 		bufferSize = 0;
116 	}
117 	if (sizeNeeded > bufferSize) {
118 		sizeNeeded = (int64) floor (sizeNeeded * 1.61803) + 100;
119 		buffer = (char *) realloc (buffer, (size_t) sizeNeeded * sizeof (char));
120 		if (buffer == nullptr) {
121 			bufferSize = 0;
122 			return "(out of memory during tracing)";
123 		}
124 		bufferSize = sizeNeeded;
125 	}
126 	int64 i, j;
127 	for (i = 0, j = 0; i < n; i ++) {
128 		char32 kar = string [i];
129 		if (kar <= 0x00'007F) {   // 7 bits
130 			buffer [j ++] = (char) (char8) kar;   // guarded truncation
131 		} else if (kar <= 0x00'07FF) {   // 11 bits
132 			buffer [j ++] = (char) (char8) (0x00'00C0 | (kar >> 6));   // the upper 5 bits yield a number between 0xC4 and 0xDF
133 			buffer [j ++] = (char) (char8) (0x00'0080 | (kar & 0x00'003F));   // the lower 6 bits yield a number between 0x80 and 0xBF
134 		} else if (kar <= 0x00'FFFF) {   // 16 bits
135 			buffer [j ++] = (char) (char8) (0x00'00E0 | (kar >> 12));   // the upper 4 bits yield a number between 0xE0 and 0xEF
136 			buffer [j ++] = (char) (char8) (0x00'0080 | ((kar >> 6) & 0x00'003F));
137 			buffer [j ++] = (char) (char8) (0x00'0080 | (kar & 0x00'003F));
138 		} else {   // 21 bits
139 			buffer [j ++] = (char) (char8) (0x00'00F0 | (kar >> 18));   // the upper 3 bits yield a number between 0xF0 and 0xF4 (0x10FFFF >> 18 == 4)
140 			buffer [j ++] = (char) (char8) (0x00'0080 | ((kar >> 12) & 0x00'003F));   // the next 6 bits
141 			buffer [j ++] = (char) (char8) (0x00'0080 | ((kar >> 6) & 0x00'003F));   // the third 6 bits
142 			buffer [j ++] = (char) (char8) (0x00'0080 | (kar & 0x00'003F));   // the lower 6 bits
143 		}
144 	}
145 	buffer [j] = '\0';
146 	return buffer;
147 }
148 #ifdef _WIN32
_peek32to16(conststring32 string)149 conststring16 MelderTrace::_peek32to16 (conststring32 string) {
150 	if (! string) return u"";
151 	static char16 *buffer { nullptr };
152 	static int64 bufferSize { 0 };
153 	int64 n = str32len (string);
154 	int64 sizeNeeded = n * 2 + 1;
155 	if ((bufferSize - sizeNeeded) * (int64) sizeof (char16) >= 10'000) {
156 		free (buffer);
157 		bufferSize = 0;
158 	}
159 	if (sizeNeeded > bufferSize) {
160 		sizeNeeded = (int64) floor (sizeNeeded * 1.61803) + 100;
161 		buffer = (char16 *) realloc (buffer, (size_t) sizeNeeded * sizeof (char16));
162 		if (! buffer) {
163 			bufferSize = 0;
164 			return u"(out of memory during tracing)";
165 		}
166 		bufferSize = sizeNeeded;
167 	}
168 	int64 i, j;
169 	for (i = 0, j = 0; i < n; i ++) {
170 		char32 kar = string [i];
171 		if (kar <= 0x00'D7FF) {   // 16 bits
172 			buffer [j ++] = (char16) kar;   // guarded truncation
173 		} else if (kar <= 0x00'DFFF) {   // 16 bits
174 			buffer [j ++] = (char16) UNICODE_REPLACEMENT_CHARACTER;   // forbidden for UTF-32
175 		} else if (kar <= 0x00'FFFF) {   // 16 bits
176 			buffer [j ++] = (char16) kar;   // guarded truncation
177 		} else if (kar <= 0x10'FFFF) {   // 21 bits
178 			kar -= 0x01'0000;
179 			buffer [j ++] = (char16) (0x00'D800 | (kar >> 10));   // guarded truncation
180 			buffer [j ++] = (char16) (0x00'DC00 | (kar & 0x00'03FF));   // guarded truncation
181 		} else {   // 21 bits
182 			buffer [j ++] = (char16) UNICODE_REPLACEMENT_CHARACTER;
183 		}
184 	}
185 	buffer [j] = u'\0';
186 	return buffer;
187 }
188 #endif
189 
190 /********** TRACE **********/
191 
Melder_tracingToFile(MelderFile file)192 void Melder_tracingToFile (MelderFile file) {
193 	MelderFile_copy (file, & MelderTrace::_file);
194 	MelderFile_delete (& MelderTrace::_file);
195 }
196 
_open(conststring8 sourceCodeFileName,int lineNumber,conststring8 functionName)197 FILE * MelderTrace::_open (conststring8 sourceCodeFileName, int lineNumber, conststring8 functionName) {
198 	FILE *f;
199 	#if defined (_WIN32) && ! defined (__CYGWIN__)
200 		f = _wfopen ((const wchar_t *) MelderTrace::_peek32to16 (MelderTrace::_file. path), L"a");
201 	#else
202 		char utf8path [kMelder_MAXPATH+1];
203 		Melder_32to8_fileSystem_inplace (MelderTrace::_file. path, utf8path);   // this Melder_xxx() function is OK to call
204 		f = fopen ((char *) utf8path, "a");
205 	#endif
206 	if (! f)
207 		f = stderr;   // if the file cannot be opened, we can still trace to stderr!
208 	if (sourceCodeFileName) {
209 		const char *slashLocation = strrchr (sourceCodeFileName, Melder_DIRECTORY_SEPARATOR);
210 		fprintf (f, "%s (%s:%d): ", functionName, slashLocation ? slashLocation + 1 : sourceCodeFileName, lineNumber);
211 	} else {
212 		fprintf (f, "%s: ", functionName);
213 	}
214 	return f;
215 }
216 
_close(FILE * f)217 void MelderTrace::_close (FILE *f) {
218 	fprintf (f, "\n");
219 	if (f != stderr)
220 		fclose (f);
221 }
222 
223 #if defined (linux) && ! defined (NO_GUI)
theGtkLogHandler(const gchar * log_domain,GLogLevelFlags log_level,const gchar * message,gpointer unused_data)224 static void theGtkLogHandler (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer unused_data) {
225 	FILE *f = MelderTrace::_open (nullptr, 0, "GTK");
226 	fprintf (f, "%s", message);
227 	MelderTrace::_close (f);
228 }
theGlibLogHandler(const gchar * log_domain,GLogLevelFlags log_level,const gchar * message,gpointer unused_data)229 static void theGlibLogHandler (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer unused_data) {
230 	FILE *f = MelderTrace::_open (nullptr, 0, "GLib");
231 	fprintf (f, "%s", message);
232 	MelderTrace::_close (f);
233 }
theGlibGobjectLogHandler(const gchar * log_domain,GLogLevelFlags log_level,const gchar * message,gpointer unused_data)234 static void theGlibGobjectLogHandler (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer unused_data) {
235 	FILE *f = MelderTrace::_open (nullptr, 0, "GLib-GObject");
236 	fprintf (f, "%s", message);
237 	MelderTrace::_close (f);
238 }
239 #endif
240 
Melder_setTracing(bool tracing)241 void Melder_setTracing (bool tracing) {
242 	time_t today = time (nullptr);
243 	#define xstr(s) str(s)
244 	#define str(s) #s
245 	if (! tracing)
246 		trace (U"switch tracing off"
247 			U" in Praat version ", Melder_peek8to32 (xstr (PRAAT_VERSION_STR)),
248 			U" at ", Melder_peek8to32 (ctime (& today))
249 		);
250 	Melder_isTracing = tracing;
251 	#if defined (linux) && ! defined (NO_GUI)
252 		static guint handler_id1, handler_id2, handler_id3;
253 		if (tracing) {
254 			handler_id1 = g_log_set_handler ("Gtk",          (GLogLevelFlags) (G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION), theGtkLogHandler,         nullptr);
255 			handler_id2 = g_log_set_handler ("GLib",         (GLogLevelFlags) (G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION), theGlibLogHandler,        nullptr);
256 			handler_id3 = g_log_set_handler ("GLib-GObject", (GLogLevelFlags) (G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION), theGlibGobjectLogHandler, nullptr);
257 		} else {
258 			if (handler_id1) g_log_remove_handler ("Gtk",          handler_id1);
259 			if (handler_id2) g_log_remove_handler ("GLib",         handler_id2);
260 			if (handler_id3) g_log_remove_handler ("GLib-GObject", handler_id3);
261 			handler_id1 = handler_id2 = handler_id3 = 0;
262 		}
263 	#endif
264 	if (tracing)
265 		trace (U"switch tracing on"
266 			U" in Praat version ", Melder_peek8to32 (xstr (PRAAT_VERSION_STR)),
267 			U" at ", Melder_peek8to32 (ctime (& today))
268 		);
269 }
270 
271 /* End of file melder_debug.cpp */
272