/src/gdal/port/cpl_error.cpp
Line | Count | Source |
1 | | |
2 | | /********************************************************************** |
3 | | * |
4 | | * Name: cpl_error.cpp |
5 | | * Project: CPL - Common Portability Library |
6 | | * Purpose: Error handling functions. |
7 | | * Author: Daniel Morissette, danmo@videotron.ca |
8 | | * |
9 | | ********************************************************************** |
10 | | * Copyright (c) 1998, Daniel Morissette |
11 | | * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com> |
12 | | * |
13 | | * SPDX-License-Identifier: MIT |
14 | | ****************************************************************************/ |
15 | | |
16 | | #include "cpl_error.h" |
17 | | |
18 | | #include <cstdarg> |
19 | | #include <cstdio> |
20 | | #include <cstdlib> |
21 | | #include <cstring> |
22 | | |
23 | | #include <algorithm> |
24 | | |
25 | | #include "cpl_config.h" |
26 | | #include "cpl_conv.h" |
27 | | #include "cpl_multiproc.h" |
28 | | #include "cpl_string.h" |
29 | | #include "cpl_vsi.h" |
30 | | #include "cpl_error_internal.h" |
31 | | |
32 | | #if !defined(va_copy) && defined(__va_copy) |
33 | | #define va_copy __va_copy |
34 | | #endif |
35 | | |
36 | | #define TIMESTAMP_DEBUG |
37 | | // #define MEMORY_DEBUG |
38 | | |
39 | | static CPLMutex *hErrorMutex = nullptr; |
40 | | static void *pErrorHandlerUserData = nullptr; |
41 | | static CPLErrorHandler pfnErrorHandler = CPLDefaultErrorHandler; |
42 | | static bool gbCatchDebug = true; |
43 | | |
44 | | constexpr int DEFAULT_LAST_ERR_MSG_SIZE = |
45 | | #if !defined(HAVE_VSNPRINTF) |
46 | | 20000 |
47 | | #else |
48 | | 500 |
49 | | #endif |
50 | | ; |
51 | | |
52 | | typedef struct errHandler |
53 | | { |
54 | | struct errHandler *psNext; |
55 | | void *pUserData; |
56 | | CPLErrorHandler pfnHandler; |
57 | | bool bCatchDebug; |
58 | | } CPLErrorHandlerNode; |
59 | | |
60 | | typedef struct |
61 | | { |
62 | | CPLErrorNum nLastErrNo; |
63 | | CPLErr eLastErrType; |
64 | | CPLErrorHandlerNode *psHandlerStack; |
65 | | int nLastErrMsgMax; |
66 | | int nFailureIntoWarning; |
67 | | bool bProgressMode; |
68 | | bool bEmitNewlineBeforeNextDbgMsg; |
69 | | GUInt32 nErrorCounter; |
70 | | char szLastErrMsg[DEFAULT_LAST_ERR_MSG_SIZE]; |
71 | | // Do not add anything here. szLastErrMsg must be the last field. |
72 | | // See CPLRealloc() below. |
73 | | } CPLErrorContext; |
74 | | |
75 | | constexpr CPLErrorContext sNoErrorContext = {0, CE_None, nullptr, 0, 0, |
76 | | false, false, 0, ""}; |
77 | | |
78 | | constexpr CPLErrorContext sWarningContext = { |
79 | | 0, CE_Warning, nullptr, 0, 0, false, false, 0, "A warning was emitted"}; |
80 | | |
81 | | constexpr CPLErrorContext sFailureContext = { |
82 | | 0, CE_Warning, nullptr, 0, 0, false, false, 0, "A failure was emitted"}; |
83 | | |
84 | | #define IS_PREFEFINED_ERROR_CTX(psCtxt) \ |
85 | 53.7k | (psCtx == &sNoErrorContext || psCtx == &sWarningContext || \ |
86 | 53.7k | psCtxt == &sFailureContext) |
87 | | |
88 | | /************************************************************************/ |
89 | | /* CPLErrorContextGetString() */ |
90 | | /************************************************************************/ |
91 | | |
92 | | // Makes clang -fsanitize=undefined happy since it doesn't like |
93 | | // dereferencing szLastErrMsg[i>=DEFAULT_LAST_ERR_MSG_SIZE] |
94 | | |
95 | | static char *CPLErrorContextGetString(CPLErrorContext *psCtxt) |
96 | 236 | { |
97 | 236 | return psCtxt->szLastErrMsg; |
98 | 236 | } |
99 | | |
100 | | /************************************************************************/ |
101 | | /* CPLGetErrorContext() */ |
102 | | /************************************************************************/ |
103 | | |
104 | | static CPLErrorContext *CPLGetErrorContext() |
105 | | |
106 | 52.4k | { |
107 | 52.4k | int bError = FALSE; |
108 | 52.4k | CPLErrorContext *psCtx = reinterpret_cast<CPLErrorContext *>( |
109 | 52.4k | CPLGetTLSEx(CTLS_ERRORCONTEXT, &bError)); |
110 | 52.4k | if (bError) |
111 | 0 | return nullptr; |
112 | | |
113 | 52.4k | if (psCtx == nullptr) |
114 | 2 | { |
115 | 2 | psCtx = static_cast<CPLErrorContext *>( |
116 | 2 | VSICalloc(sizeof(CPLErrorContext), 1)); |
117 | 2 | if (psCtx == nullptr) |
118 | 0 | { |
119 | 0 | fprintf(stderr, "Out of memory attempting to report error.\n"); |
120 | 0 | return nullptr; |
121 | 0 | } |
122 | 2 | psCtx->eLastErrType = CE_None; |
123 | 2 | psCtx->nLastErrMsgMax = sizeof(psCtx->szLastErrMsg); |
124 | 2 | CPLSetTLS(CTLS_ERRORCONTEXT, psCtx, TRUE); |
125 | 2 | } |
126 | | |
127 | 52.4k | return psCtx; |
128 | 52.4k | } |
129 | | |
130 | | /************************************************************************/ |
131 | | /* CPLGetErrorHandlerUserData() */ |
132 | | /************************************************************************/ |
133 | | |
134 | | /** |
135 | | * Fetch the user data for the error context |
136 | | * |
137 | | * Fetches the user data for the current error context. You can |
138 | | * set the user data for the error context when you add your handler by |
139 | | * issuing CPLSetErrorHandlerEx() and CPLPushErrorHandlerEx(). Note that |
140 | | * user data is primarily intended for providing context within error handlers |
141 | | * themselves, but they could potentially be abused in other useful ways with |
142 | | * the usual caveat emptor understanding. |
143 | | * |
144 | | * @return the user data pointer for the error context |
145 | | */ |
146 | | |
147 | | void *CPL_STDCALL CPLGetErrorHandlerUserData(void) |
148 | 0 | { |
149 | | // get the current threadlocal or global error context user data |
150 | 0 | CPLErrorContext *psCtx = CPLGetErrorContext(); |
151 | 0 | if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx)) |
152 | 0 | abort(); |
153 | 0 | return reinterpret_cast<void *>(psCtx->psHandlerStack |
154 | 0 | ? psCtx->psHandlerStack->pUserData |
155 | 0 | : pErrorHandlerUserData); |
156 | 0 | } |
157 | | |
158 | | /************************************************************************/ |
159 | | /* CPLGetErrorHandler() */ |
160 | | /************************************************************************/ |
161 | | |
162 | | /** |
163 | | * Fetch the current error handler for the current error context. |
164 | | * |
165 | | * This will be the last error handler pushed in the thread-local error stack |
166 | | * with CPLPushErrorHandler()/CPLPushErrorHandlerEx(), or if the stack is |
167 | | * empty, the global error handler set with |
168 | | * CPLSetErrorHandler()/CPLSetErrorHandlerEx(), or the default global error |
169 | | * handler. |
170 | | * |
171 | | * @param[out] ppUserData Pointer to store the user data pointer. May be NULL |
172 | | * @since GDAL 3.7 |
173 | | */ |
174 | | |
175 | | CPLErrorHandler CPLGetErrorHandler(void **ppUserData) |
176 | 0 | { |
177 | 0 | CPLErrorContext *psCtx = CPLGetErrorContext(); |
178 | |
|
179 | 0 | if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx)) |
180 | 0 | { |
181 | 0 | fprintf(stderr, "CPLGetErrorHandler() failed.\n"); |
182 | 0 | if (ppUserData) |
183 | 0 | *ppUserData = nullptr; |
184 | 0 | return CPLDefaultErrorHandler; |
185 | 0 | } |
186 | | |
187 | 0 | if (psCtx->psHandlerStack != nullptr) |
188 | 0 | { |
189 | 0 | if (ppUserData) |
190 | 0 | *ppUserData = psCtx->psHandlerStack->pUserData; |
191 | 0 | return psCtx->psHandlerStack->pfnHandler; |
192 | 0 | } |
193 | | |
194 | 0 | CPLMutexHolderD(&hErrorMutex); |
195 | 0 | if (ppUserData) |
196 | 0 | *ppUserData = pErrorHandlerUserData; |
197 | 0 | return pfnErrorHandler; |
198 | 0 | } |
199 | | |
200 | | /************************************************************************/ |
201 | | /* ApplyErrorHandler() */ |
202 | | /************************************************************************/ |
203 | | |
204 | | static void ApplyErrorHandler(CPLErrorContext *psCtx, CPLErr eErrClass, |
205 | | CPLErrorNum err_no, const char *pszMessage) |
206 | 24.6k | { |
207 | 24.6k | bool bProcessed = false; |
208 | | |
209 | 24.6k | if (psCtx->psHandlerStack != nullptr) |
210 | 0 | { |
211 | | // iterate through the threadlocal handler stack |
212 | 0 | if ((eErrClass != CE_Debug) || psCtx->psHandlerStack->bCatchDebug) |
213 | 0 | { |
214 | | // call the error handler |
215 | 0 | CPLErrorHandlerNode *psNewCurNode = psCtx->psHandlerStack; |
216 | 0 | psCtx->psHandlerStack->pfnHandler(eErrClass, err_no, pszMessage); |
217 | 0 | if (psNewCurNode != psCtx->psHandlerStack) |
218 | 0 | { |
219 | 0 | fprintf(stderr, "ApplyErrorHandler() has detected that a " |
220 | 0 | "previous error handler messed up with the " |
221 | 0 | "error stack. Chaos guaranteed!\n"); |
222 | 0 | } |
223 | 0 | bProcessed = true; |
224 | 0 | } |
225 | 0 | else |
226 | 0 | { |
227 | | // need to iterate to a parent handler for debug messages |
228 | 0 | CPLErrorHandlerNode *psNode = psCtx->psHandlerStack->psNext; |
229 | 0 | while (psNode != nullptr) |
230 | 0 | { |
231 | 0 | if (psNode->bCatchDebug) |
232 | 0 | { |
233 | 0 | CPLErrorHandlerNode *psBackupCurNode = |
234 | 0 | psCtx->psHandlerStack; |
235 | 0 | psCtx->psHandlerStack = psNode; |
236 | 0 | CPLErrorHandlerNode *psNewCurNode = psCtx->psHandlerStack; |
237 | 0 | psNode->pfnHandler(eErrClass, err_no, pszMessage); |
238 | | // cppcheck-suppress knownConditionTrueFalse |
239 | 0 | if (psNewCurNode != psCtx->psHandlerStack) |
240 | 0 | { |
241 | 0 | fprintf(stderr, |
242 | 0 | "ApplyErrorHandler() has detected that a " |
243 | 0 | "previous error handler messed up with the " |
244 | 0 | "error stack. Chaos guaranteed!\n"); |
245 | 0 | } |
246 | 0 | psCtx->psHandlerStack = psBackupCurNode; |
247 | 0 | bProcessed = true; |
248 | 0 | break; |
249 | 0 | } |
250 | 0 | psNode = psNode->psNext; |
251 | 0 | } |
252 | 0 | } |
253 | 0 | } |
254 | | |
255 | 24.6k | if (!bProcessed) |
256 | 24.6k | { |
257 | | // hit the global error handler |
258 | 24.6k | CPLMutexHolderD(&hErrorMutex); |
259 | 24.6k | if ((eErrClass != CE_Debug) || gbCatchDebug) |
260 | 24.6k | { |
261 | 24.6k | if (pfnErrorHandler != nullptr) |
262 | 24.6k | { |
263 | 24.6k | pfnErrorHandler(eErrClass, err_no, pszMessage); |
264 | 24.6k | } |
265 | 24.6k | } |
266 | 0 | else /* if( eErrClass == CE_Debug ) */ |
267 | 0 | { |
268 | | // for CPLDebug messages we propagate to the default error handler |
269 | 0 | CPLDefaultErrorHandler(eErrClass, err_no, pszMessage); |
270 | 0 | } |
271 | 24.6k | } |
272 | 24.6k | } |
273 | | |
274 | | /********************************************************************** |
275 | | * CPLError() |
276 | | **********************************************************************/ |
277 | | |
278 | | /** |
279 | | * Report an error. |
280 | | * |
281 | | * This function reports an error in a manner that can be hooked |
282 | | * and reported appropriate by different applications. |
283 | | * |
284 | | * The effect of this function can be altered by applications by installing |
285 | | * a custom error handling using CPLSetErrorHandler(). |
286 | | * |
287 | | * The eErrClass argument can have the value CE_Warning indicating that the |
288 | | * message is an informational warning, CE_Failure indicating that the |
289 | | * action failed, but that normal recover mechanisms will be used or |
290 | | * CE_Fatal meaning that a fatal error has occurred, and that CPLError() |
291 | | * should not return. |
292 | | * |
293 | | * The default behavior of CPLError() is to report errors to stderr, |
294 | | * and to abort() after reporting a CE_Fatal error. It is expected that |
295 | | * some applications will want to suppress error reporting, and will want to |
296 | | * install a C++ exception, or longjmp() approach to no local fatal error |
297 | | * recovery. |
298 | | * |
299 | | * Regardless of how application error handlers or the default error |
300 | | * handler choose to handle an error, the error number, and message will |
301 | | * be stored for recovery with CPLGetLastErrorNo() and CPLGetLastErrorMsg(). |
302 | | * |
303 | | * @param eErrClass one of CE_Warning, CE_Failure or CE_Fatal. |
304 | | * @param err_no the error number (CPLE_*) from cpl_error.h. |
305 | | * @param fmt a printf() style format string. Any additional arguments |
306 | | * will be treated as arguments to fill in this format in a manner |
307 | | * similar to printf(). |
308 | | */ |
309 | | |
310 | | void CPLError(CPLErr eErrClass, CPLErrorNum err_no, |
311 | | CPL_FORMAT_STRING(const char *fmt), ...) |
312 | 22.9k | { |
313 | 22.9k | va_list args; |
314 | | |
315 | | // Expand the error message. |
316 | 22.9k | va_start(args, fmt); |
317 | 22.9k | CPLErrorV(eErrClass, err_no, fmt, args); |
318 | 22.9k | va_end(args); |
319 | 22.9k | } |
320 | | |
321 | | /************************************************************************/ |
322 | | /* CPLErrorV() */ |
323 | | /************************************************************************/ |
324 | | |
325 | | /** Same as CPLError() but with a va_list */ |
326 | | void CPLErrorV(CPLErr eErrClass, CPLErrorNum err_no, const char *fmt, |
327 | | va_list args) |
328 | 22.9k | { |
329 | 22.9k | CPLErrorContext *psCtx = CPLGetErrorContext(); |
330 | 22.9k | if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx)) |
331 | 0 | { |
332 | 0 | int bMemoryError = FALSE; |
333 | 0 | if (eErrClass == CE_Warning) |
334 | 0 | { |
335 | 0 | CPLSetTLSWithFreeFuncEx( |
336 | 0 | CTLS_ERRORCONTEXT, |
337 | 0 | reinterpret_cast<void *>( |
338 | 0 | const_cast<CPLErrorContext *>(&sWarningContext)), |
339 | 0 | nullptr, &bMemoryError); |
340 | 0 | } |
341 | 0 | else if (eErrClass == CE_Failure) |
342 | 0 | { |
343 | 0 | CPLSetTLSWithFreeFuncEx( |
344 | 0 | CTLS_ERRORCONTEXT, |
345 | 0 | reinterpret_cast<void *>( |
346 | 0 | const_cast<CPLErrorContext *>(&sFailureContext)), |
347 | 0 | nullptr, &bMemoryError); |
348 | 0 | } |
349 | | |
350 | | // TODO: Is it possible to move the entire szShortMessage under the if |
351 | | // pfnErrorHandler? |
352 | 0 | char szShortMessage[80] = {}; |
353 | 0 | CPLvsnprintf(szShortMessage, sizeof(szShortMessage), fmt, args); |
354 | |
|
355 | 0 | CPLMutexHolderD(&hErrorMutex); |
356 | 0 | if (pfnErrorHandler != nullptr) |
357 | 0 | pfnErrorHandler(eErrClass, err_no, szShortMessage); |
358 | 0 | return; |
359 | 0 | } |
360 | | |
361 | 22.9k | if (psCtx->nFailureIntoWarning > 0 && eErrClass == CE_Failure) |
362 | 0 | eErrClass = CE_Warning; |
363 | | |
364 | | /* -------------------------------------------------------------------- */ |
365 | | /* Expand the error message */ |
366 | | /* -------------------------------------------------------------------- */ |
367 | 22.9k | #if defined(HAVE_VSNPRINTF) |
368 | 22.9k | { |
369 | 22.9k | va_list wrk_args; |
370 | | |
371 | 22.9k | #ifdef va_copy |
372 | 22.9k | va_copy(wrk_args, args); |
373 | | #else |
374 | | wrk_args = args; |
375 | | #endif |
376 | | |
377 | | /* -------------------------------------------------------------------- |
378 | | */ |
379 | | /* If CPL_ACCUM_ERROR_MSG=ON accumulate the error messages, */ |
380 | | /* rather than just replacing the last error message. */ |
381 | | /* -------------------------------------------------------------------- |
382 | | */ |
383 | 22.9k | int nPreviousSize = 0; |
384 | 22.9k | if (psCtx->psHandlerStack != nullptr && |
385 | 0 | EQUAL(CPLGetConfigOption("CPL_ACCUM_ERROR_MSG", ""), "ON")) |
386 | 0 | { |
387 | 0 | nPreviousSize = static_cast<int>(strlen(psCtx->szLastErrMsg)); |
388 | 0 | if (nPreviousSize) |
389 | 0 | { |
390 | 0 | if (nPreviousSize + 1 + 1 >= psCtx->nLastErrMsgMax) |
391 | 0 | { |
392 | 0 | psCtx->nLastErrMsgMax *= 3; |
393 | 0 | psCtx = static_cast<CPLErrorContext *>( |
394 | 0 | CPLRealloc(psCtx, sizeof(CPLErrorContext) - |
395 | 0 | DEFAULT_LAST_ERR_MSG_SIZE + |
396 | 0 | psCtx->nLastErrMsgMax + 1)); |
397 | 0 | CPLSetTLS(CTLS_ERRORCONTEXT, psCtx, TRUE); |
398 | 0 | } |
399 | 0 | char *pszLastErrMsg = CPLErrorContextGetString(psCtx); |
400 | 0 | pszLastErrMsg[nPreviousSize] = '\n'; |
401 | 0 | pszLastErrMsg[nPreviousSize + 1] = '\0'; |
402 | 0 | nPreviousSize++; |
403 | 0 | } |
404 | 0 | } |
405 | | |
406 | 22.9k | int nPR = 0; |
407 | 22.9k | while (((nPR = CPLvsnprintf(psCtx->szLastErrMsg + nPreviousSize, |
408 | 22.9k | psCtx->nLastErrMsgMax - nPreviousSize, fmt, |
409 | 22.9k | wrk_args)) == -1 || |
410 | 22.9k | nPR >= psCtx->nLastErrMsgMax - nPreviousSize - 1) && |
411 | 6 | psCtx->nLastErrMsgMax < 1000000) |
412 | 6 | { |
413 | 6 | #ifdef va_copy |
414 | 6 | va_end(wrk_args); |
415 | 6 | va_copy(wrk_args, args); |
416 | | #else |
417 | | wrk_args = args; |
418 | | #endif |
419 | 6 | psCtx->nLastErrMsgMax *= 3; |
420 | 6 | psCtx = static_cast<CPLErrorContext *>(CPLRealloc( |
421 | 6 | psCtx, sizeof(CPLErrorContext) - DEFAULT_LAST_ERR_MSG_SIZE + |
422 | 6 | psCtx->nLastErrMsgMax + 1)); |
423 | 6 | CPLSetTLS(CTLS_ERRORCONTEXT, psCtx, TRUE); |
424 | 6 | } |
425 | | |
426 | 22.9k | va_end(wrk_args); |
427 | 22.9k | } |
428 | | #else |
429 | | // !HAVE_VSNPRINTF |
430 | | CPLvsnprintf(psCtx->szLastErrMsg, psCtx->nLastErrMsgMax, fmt, args); |
431 | | #endif |
432 | | |
433 | | /* -------------------------------------------------------------------- */ |
434 | | /* Obfuscate any password in error message */ |
435 | | /* -------------------------------------------------------------------- */ |
436 | 22.9k | char *pszPassword = strstr(psCtx->szLastErrMsg, "password="); |
437 | 22.9k | if (pszPassword != nullptr) |
438 | 140 | { |
439 | 140 | char *pszIter = pszPassword + strlen("password="); |
440 | 7.20k | while (*pszIter != ' ' && *pszIter != '\0') |
441 | 7.06k | { |
442 | 7.06k | *pszIter = 'X'; |
443 | 7.06k | pszIter++; |
444 | 7.06k | } |
445 | 140 | } |
446 | | |
447 | | /* -------------------------------------------------------------------- */ |
448 | | /* If the user provided an handling function, then */ |
449 | | /* call it, otherwise print the error to stderr and return. */ |
450 | | /* -------------------------------------------------------------------- */ |
451 | 22.9k | psCtx->nLastErrNo = err_no; |
452 | 22.9k | psCtx->eLastErrType = eErrClass; |
453 | 22.9k | if (psCtx->nErrorCounter == ~(0U)) |
454 | 0 | psCtx->nErrorCounter = 0; |
455 | 22.9k | else |
456 | 22.9k | psCtx->nErrorCounter++; |
457 | | |
458 | 22.9k | if (CPLGetConfigOption("CPL_LOG_ERRORS", nullptr) != nullptr) |
459 | 6.12k | CPLDebug("CPLError", "%s", psCtx->szLastErrMsg); |
460 | | |
461 | | /* -------------------------------------------------------------------- */ |
462 | | /* Invoke the current error handler. */ |
463 | | /* -------------------------------------------------------------------- */ |
464 | 22.9k | ApplyErrorHandler(psCtx, eErrClass, err_no, psCtx->szLastErrMsg); |
465 | | |
466 | 22.9k | if (eErrClass == CE_Fatal) |
467 | 0 | abort(); |
468 | 22.9k | } |
469 | | |
470 | | /************************************************************************/ |
471 | | /* CPLEmergencyError() */ |
472 | | /************************************************************************/ |
473 | | |
474 | | /** |
475 | | * Fatal error when things are bad. |
476 | | * |
477 | | * This function should be called in an emergency situation where |
478 | | * it is unlikely that a regular error report would work. This would |
479 | | * include in the case of heap exhaustion for even small allocations, |
480 | | * or any failure in the process of reporting an error (such as TLS |
481 | | * allocations). |
482 | | * |
483 | | * This function should never return. After the error message has been |
484 | | * reported as best possible, the application will abort() similarly to how |
485 | | * CPLError() aborts on CE_Fatal class errors. |
486 | | * |
487 | | * @param pszMessage the error message to report. |
488 | | */ |
489 | | |
490 | | void CPLEmergencyError(const char *pszMessage) |
491 | 0 | { |
492 | 0 | static bool bInEmergencyError = false; |
493 | | |
494 | | // If we are already in emergency error then one of the |
495 | | // following failed, so avoid them the second time through. |
496 | 0 | if (!bInEmergencyError) |
497 | 0 | { |
498 | 0 | bInEmergencyError = true; |
499 | 0 | CPLErrorContext *psCtx = |
500 | 0 | static_cast<CPLErrorContext *>(CPLGetTLS(CTLS_ERRORCONTEXT)); |
501 | |
|
502 | 0 | ApplyErrorHandler(psCtx, CE_Fatal, CPLE_AppDefined, pszMessage); |
503 | 0 | } |
504 | | |
505 | | // Ultimate fallback. |
506 | 0 | fprintf(stderr, "FATAL: %s\n", pszMessage); |
507 | |
|
508 | 0 | abort(); |
509 | 0 | } |
510 | | |
511 | | /************************************************************************/ |
512 | | /* CPLGetProcessMemorySize() */ |
513 | | /************************************************************************/ |
514 | | |
515 | | #ifdef MEMORY_DEBUG |
516 | | |
517 | | #ifdef __linux |
518 | | static int CPLGetProcessMemorySize() |
519 | | { |
520 | | FILE *fp = fopen("/proc/self/status", "r"); |
521 | | if (fp == nullptr) |
522 | | return -1; |
523 | | int nRet = -1; |
524 | | char szLine[128] = {}; |
525 | | while (fgets(szLine, sizeof(szLine), fp) != nullptr) |
526 | | { |
527 | | if (STARTS_WITH(szLine, "VmSize:")) |
528 | | { |
529 | | const char *pszPtr = szLine; |
530 | | while (!(*pszPtr == '\0' || (*pszPtr >= '0' && *pszPtr <= '9'))) |
531 | | pszPtr++; |
532 | | nRet = atoi(pszPtr); |
533 | | break; |
534 | | } |
535 | | } |
536 | | fclose(fp); |
537 | | return nRet; |
538 | | } |
539 | | #else |
540 | | #error CPLGetProcessMemorySize() unimplemented for this OS |
541 | | #endif |
542 | | |
543 | | #endif // def MEMORY_DEBUG |
544 | | |
545 | | /************************************************************************/ |
546 | | /* CPLGettimeofday() */ |
547 | | /************************************************************************/ |
548 | | |
549 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
550 | | #include <sys/timeb.h> |
551 | | |
552 | | namespace |
553 | | { |
554 | | struct CPLTimeVal |
555 | | { |
556 | | time_t tv_sec; /* seconds */ |
557 | | long tv_usec; /* and microseconds */ |
558 | | }; |
559 | | } // namespace |
560 | | |
561 | | static int CPLGettimeofday(struct CPLTimeVal *tp, void * /* timezonep*/) |
562 | | { |
563 | | struct _timeb theTime; |
564 | | |
565 | | _ftime(&theTime); |
566 | | tp->tv_sec = static_cast<time_t>(theTime.time); |
567 | | tp->tv_usec = theTime.millitm * 1000; |
568 | | return 0; |
569 | | } |
570 | | #else |
571 | | #include <sys/time.h> /* for gettimeofday() */ |
572 | | #define CPLTimeVal timeval |
573 | 3.09k | #define CPLGettimeofday(t, u) gettimeofday(t, u) |
574 | | #endif |
575 | | |
576 | | #ifndef WITHOUT_CPLDEBUG |
577 | | |
578 | | /************************************************************************/ |
579 | | /* CPLvDebug() */ |
580 | | /************************************************************************/ |
581 | | |
582 | | static void CPLvDebug(const char *pszCategory, |
583 | | CPL_FORMAT_STRING(const char *pszFormat), va_list args) |
584 | 27.1k | { |
585 | 27.1k | CPLErrorContext *psCtx = CPLGetErrorContext(); |
586 | 27.1k | if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx)) |
587 | 0 | return; |
588 | 27.1k | const char *pszDebug = CPLGetConfigOption("CPL_DEBUG", nullptr); |
589 | | |
590 | | /* -------------------------------------------------------------------- */ |
591 | | /* Does this message pass our current criteria? */ |
592 | | /* -------------------------------------------------------------------- */ |
593 | 27.1k | if (pszDebug == nullptr || EQUAL(pszDebug, "NO") || |
594 | 17.3k | EQUAL(pszDebug, "OFF") || EQUAL(pszDebug, "FALSE") || |
595 | 17.3k | EQUAL(pszDebug, "0")) |
596 | 9.78k | { |
597 | 9.78k | return; |
598 | 9.78k | } |
599 | | |
600 | 17.3k | if (!EQUAL(pszDebug, "ON") && !EQUAL(pszDebug, "YES") && |
601 | 16.7k | !EQUAL(pszDebug, "TRUE") && !EQUAL(pszDebug, "1") && |
602 | 16.3k | !EQUAL(pszDebug, "")) |
603 | 15.9k | { |
604 | | // check if value of CPL_DEBUG contains the category |
605 | 15.9k | const size_t nLen = strlen(pszCategory); |
606 | | |
607 | 15.9k | size_t i = 0; |
608 | 131k | for (i = 0; pszDebug[i] != '\0'; i++) |
609 | 115k | { |
610 | 115k | if (EQUALN(pszCategory, pszDebug + i, nLen)) |
611 | 233 | break; |
612 | 115k | } |
613 | | |
614 | 15.9k | if (pszDebug[i] == '\0') |
615 | 15.6k | return; |
616 | 15.9k | } |
617 | | |
618 | | /* -------------------------------------------------------------------- */ |
619 | | /* Allocate a block for the error. */ |
620 | | /* -------------------------------------------------------------------- */ |
621 | 1.69k | const int ERROR_MAX = 25000; |
622 | 1.69k | char *pszMessage = static_cast<char *>(VSIMalloc(ERROR_MAX)); |
623 | 1.69k | if (pszMessage == nullptr) |
624 | 0 | return; |
625 | | |
626 | | /* -------------------------------------------------------------------- */ |
627 | | /* Dal -- always log a timestamp as the first part of the line */ |
628 | | /* to ensure one is looking at what one should be looking at! */ |
629 | | /* -------------------------------------------------------------------- */ |
630 | | |
631 | 1.69k | pszMessage[0] = '\0'; |
632 | 1.69k | #ifdef TIMESTAMP_DEBUG |
633 | 1.69k | if (CPLTestBool(CPLGetConfigOption("CPL_TIMESTAMP", "NO"))) |
634 | 1.54k | { |
635 | 1.54k | static struct CPLTimeVal tvStart; |
636 | 1.54k | static const auto unused = CPLGettimeofday(&tvStart, nullptr); |
637 | 1.54k | CPL_IGNORE_RET_VAL(unused); |
638 | 1.54k | struct CPLTimeVal tv; |
639 | 1.54k | CPLGettimeofday(&tv, nullptr); |
640 | 1.54k | strcpy(pszMessage, "["); |
641 | 1.54k | strcat(pszMessage, VSICTime(static_cast<unsigned long>(tv.tv_sec))); |
642 | | |
643 | | // On windows anyway, ctime puts a \n at the end, but I'm not |
644 | | // convinced this is standard behavior, so we'll get rid of it |
645 | | // carefully |
646 | | |
647 | 1.54k | if (pszMessage[strlen(pszMessage) - 1] == '\n') |
648 | 1.54k | { |
649 | 1.54k | pszMessage[strlen(pszMessage) - 1] = 0; // blow it out |
650 | 1.54k | } |
651 | 1.54k | CPLsnprintf(pszMessage + strlen(pszMessage), |
652 | 1.54k | ERROR_MAX - strlen(pszMessage), |
653 | 1.54k | "].%04d, %03.04f: ", static_cast<int>(tv.tv_usec / 100), |
654 | 1.54k | tv.tv_sec + tv.tv_usec * 1e-6 - |
655 | 1.54k | (tvStart.tv_sec + tvStart.tv_usec * 1e-6)); |
656 | 1.54k | } |
657 | 1.69k | #endif |
658 | | |
659 | | /* -------------------------------------------------------------------- */ |
660 | | /* Add the process memory size. */ |
661 | | /* -------------------------------------------------------------------- */ |
662 | | #ifdef MEMORY_DEBUG |
663 | | char szVmSize[32] = {}; |
664 | | CPLsprintf(szVmSize, "[VmSize: %d] ", CPLGetProcessMemorySize()); |
665 | | strcat(pszMessage, szVmSize); |
666 | | #endif |
667 | | |
668 | | /* -------------------------------------------------------------------- */ |
669 | | /* Add the category. */ |
670 | | /* -------------------------------------------------------------------- */ |
671 | 1.69k | strcat(pszMessage, pszCategory); |
672 | 1.69k | strcat(pszMessage, ": "); |
673 | | |
674 | | /* -------------------------------------------------------------------- */ |
675 | | /* Format the application provided portion of the debug message. */ |
676 | | /* -------------------------------------------------------------------- */ |
677 | 1.69k | CPLvsnprintf(pszMessage + strlen(pszMessage), |
678 | 1.69k | ERROR_MAX - strlen(pszMessage), pszFormat, args); |
679 | | |
680 | | /* -------------------------------------------------------------------- */ |
681 | | /* Obfuscate any password in error message */ |
682 | | /* -------------------------------------------------------------------- */ |
683 | | |
684 | 1.69k | char *pszPassword = strstr(pszMessage, "password="); |
685 | 1.69k | if (pszPassword != nullptr) |
686 | 45 | { |
687 | 45 | char *pszIter = pszPassword + strlen("password="); |
688 | 2.68k | while (*pszIter != ' ' && *pszIter != '\0') |
689 | 2.64k | { |
690 | 2.64k | *pszIter = 'X'; |
691 | 2.64k | pszIter++; |
692 | 2.64k | } |
693 | 45 | } |
694 | | |
695 | | /* -------------------------------------------------------------------- */ |
696 | | /* Invoke the current error handler. */ |
697 | | /* -------------------------------------------------------------------- */ |
698 | 1.69k | ApplyErrorHandler(psCtx, CE_Debug, CPLE_None, pszMessage); |
699 | | |
700 | 1.69k | VSIFree(pszMessage); |
701 | 1.69k | } |
702 | | |
703 | | #endif // !WITHOUT_CPLDEBUG |
704 | | |
705 | | /************************************************************************/ |
706 | | /* CPLDebug() */ |
707 | | /************************************************************************/ |
708 | | |
709 | | /** |
710 | | * Display a debugging message. |
711 | | * |
712 | | * The category argument is used in conjunction with the CPL_DEBUG |
713 | | * environment variable to establish if the message should be displayed. |
714 | | * If the CPL_DEBUG environment variable is not set, no debug messages |
715 | | * are emitted (use CPLError(CE_Warning, ...) to ensure messages are displayed). |
716 | | * If CPL_DEBUG is set, but is an empty string or the word "ON" then all |
717 | | * debug messages are shown. Otherwise only messages whose category appears |
718 | | * somewhere within the CPL_DEBUG value are displayed (as determined by |
719 | | * strstr()). |
720 | | * |
721 | | * Categories are usually an identifier for the subsystem producing the |
722 | | * error. For instance "GDAL" might be used for the GDAL core, and "TIFF" |
723 | | * for messages from the TIFF translator. |
724 | | * |
725 | | * @param pszCategory name of the debugging message category. |
726 | | * @param pszFormat printf() style format string for message to display. |
727 | | * Remaining arguments are assumed to be for format. |
728 | | */ |
729 | | |
730 | | #ifdef WITHOUT_CPLDEBUG |
731 | | // Do not include CPLDebug. Only available in custom builds. |
732 | | #else |
733 | | |
734 | | void CPLDebug(const char *pszCategory, CPL_FORMAT_STRING(const char *pszFormat), |
735 | | ...) |
736 | | |
737 | 27.1k | { |
738 | 27.1k | va_list args; |
739 | 27.1k | va_start(args, pszFormat); |
740 | 27.1k | CPLvDebug(pszCategory, pszFormat, args); |
741 | 27.1k | va_end(args); |
742 | 27.1k | } |
743 | | |
744 | | #endif // WITHOUT_CPLDEBUG |
745 | | |
746 | | /************************************************************************/ |
747 | | /* CPLDebugProgress() */ |
748 | | /************************************************************************/ |
749 | | |
750 | | /** |
751 | | * Display a debugging message indicating a progression. |
752 | | * |
753 | | * This is the same as CPLDebug(), except that when displaying on the terminal, |
754 | | * it will erase the previous debug progress message. This is for example |
755 | | * appropriate to display increasing percentages for a task. |
756 | | * |
757 | | * The category argument is used in conjunction with the CPL_DEBUG |
758 | | * environment variable to establish if the message should be displayed. |
759 | | * If the CPL_DEBUG environment variable is not set, no debug messages |
760 | | * are emitted (use CPLError(CE_Warning, ...) to ensure messages are displayed). |
761 | | * If CPL_DEBUG is set, but is an empty string or the word "ON" then all |
762 | | * debug messages are shown. Otherwise only messages whose category appears |
763 | | * somewhere within the CPL_DEBUG value are displayed (as determined by |
764 | | * strstr()). |
765 | | * |
766 | | * Categories are usually an identifier for the subsystem producing the |
767 | | * error. For instance "GDAL" might be used for the GDAL core, and "TIFF" |
768 | | * for messages from the TIFF translator. |
769 | | * |
770 | | * @param pszCategory name of the debugging message category. |
771 | | * @param pszFormat printf() style format string for message to display. |
772 | | * Remaining arguments are assumed to be for format. |
773 | | * @since 3.9 |
774 | | */ |
775 | | |
776 | | #ifdef WITHOUT_CPLDEBUG |
777 | | // Do not include CPLDebugProgress. Only available in custom builds. |
778 | | #else |
779 | | void CPLDebugProgress(const char *pszCategory, |
780 | | CPL_FORMAT_STRING(const char *pszFormat), ...) |
781 | | |
782 | 0 | { |
783 | 0 | CPLErrorContext *psCtx = CPLGetErrorContext(); |
784 | 0 | if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx)) |
785 | 0 | return; |
786 | | |
787 | 0 | psCtx->bProgressMode = true; |
788 | |
|
789 | 0 | va_list args; |
790 | 0 | va_start(args, pszFormat); |
791 | 0 | CPLvDebug(pszCategory, pszFormat, args); |
792 | 0 | va_end(args); |
793 | |
|
794 | 0 | psCtx->bProgressMode = false; |
795 | 0 | } |
796 | | #endif // !WITHOUT_CPLDEBUG |
797 | | |
798 | | /********************************************************************** |
799 | | * CPLErrorReset() |
800 | | **********************************************************************/ |
801 | | |
802 | | /** |
803 | | * Erase any traces of previous errors. |
804 | | * |
805 | | * This is normally used to ensure that an error which has been recovered |
806 | | * from does not appear to be still in play with high level functions. |
807 | | */ |
808 | | |
809 | | void CPL_STDCALL CPLErrorReset() |
810 | 0 | { |
811 | 0 | CPLErrorContext *psCtx = CPLGetErrorContext(); |
812 | 0 | if (psCtx == nullptr) |
813 | 0 | return; |
814 | 0 | if (IS_PREFEFINED_ERROR_CTX(psCtx)) |
815 | 0 | { |
816 | 0 | int bMemoryError = FALSE; |
817 | 0 | CPLSetTLSWithFreeFuncEx( |
818 | 0 | CTLS_ERRORCONTEXT, |
819 | 0 | reinterpret_cast<void *>( |
820 | 0 | const_cast<CPLErrorContext *>(&sNoErrorContext)), |
821 | 0 | nullptr, &bMemoryError); |
822 | 0 | return; |
823 | 0 | } |
824 | | |
825 | 0 | psCtx->nLastErrNo = CPLE_None; |
826 | 0 | psCtx->szLastErrMsg[0] = '\0'; |
827 | 0 | psCtx->eLastErrType = CE_None; |
828 | 0 | psCtx->nErrorCounter = 0; |
829 | 0 | } |
830 | | |
831 | | /********************************************************************** |
832 | | * CPLErrorSetState() |
833 | | **********************************************************************/ |
834 | | |
835 | | void CPLErrorSetState(CPLErr eErrClass, CPLErrorNum err_no, const char *pszMsg, |
836 | | const GUInt32 *pnErrorCounter) |
837 | 236 | { |
838 | 236 | CPLErrorContext *psCtx = CPLGetErrorContext(); |
839 | 236 | if (psCtx == nullptr) |
840 | 0 | return; |
841 | 236 | if (IS_PREFEFINED_ERROR_CTX(psCtx)) |
842 | 0 | { |
843 | 0 | int bMemoryError = FALSE; |
844 | 0 | if (eErrClass == CE_None) |
845 | 0 | CPLSetTLSWithFreeFuncEx( |
846 | 0 | CTLS_ERRORCONTEXT, |
847 | 0 | reinterpret_cast<void *>( |
848 | 0 | const_cast<CPLErrorContext *>(&sNoErrorContext)), |
849 | 0 | nullptr, &bMemoryError); |
850 | 0 | else if (eErrClass == CE_Warning) |
851 | 0 | CPLSetTLSWithFreeFuncEx( |
852 | 0 | CTLS_ERRORCONTEXT, |
853 | 0 | reinterpret_cast<void *>( |
854 | 0 | const_cast<CPLErrorContext *>(&sWarningContext)), |
855 | 0 | nullptr, &bMemoryError); |
856 | 0 | else if (eErrClass == CE_Failure) |
857 | 0 | CPLSetTLSWithFreeFuncEx( |
858 | 0 | CTLS_ERRORCONTEXT, |
859 | 0 | reinterpret_cast<void *>( |
860 | 0 | const_cast<CPLErrorContext *>(&sFailureContext)), |
861 | 0 | nullptr, &bMemoryError); |
862 | 0 | return; |
863 | 0 | } |
864 | | |
865 | 236 | psCtx->nLastErrNo = err_no; |
866 | 236 | const size_t size = std::min(static_cast<size_t>(psCtx->nLastErrMsgMax - 1), |
867 | 236 | strlen(pszMsg)); |
868 | 236 | char *pszLastErrMsg = CPLErrorContextGetString(psCtx); |
869 | 236 | memcpy(pszLastErrMsg, pszMsg, size); |
870 | 236 | pszLastErrMsg[size] = '\0'; |
871 | 236 | psCtx->eLastErrType = eErrClass; |
872 | 236 | if (pnErrorCounter) |
873 | 0 | psCtx->nErrorCounter = *pnErrorCounter; |
874 | 236 | } |
875 | | |
876 | | /** |
877 | | * Restore an error state, without emitting an error. |
878 | | * |
879 | | * Can be useful if a routine might call CPLErrorReset() and one wants to |
880 | | * preserve the previous error state. |
881 | | * |
882 | | */ |
883 | | |
884 | | void CPL_DLL CPLErrorSetState(CPLErr eErrClass, CPLErrorNum err_no, |
885 | | const char *pszMsg) |
886 | 236 | { |
887 | 236 | CPLErrorSetState(eErrClass, err_no, pszMsg, nullptr); |
888 | 236 | } |
889 | | |
890 | | /********************************************************************** |
891 | | * CPLGetLastErrorNo() |
892 | | **********************************************************************/ |
893 | | |
894 | | /** |
895 | | * Fetch the last error number. |
896 | | * |
897 | | * Fetches the last error number posted with CPLError(), that hasn't |
898 | | * been cleared by CPLErrorReset(). This is the error number, not the error |
899 | | * class. |
900 | | * |
901 | | * @return the error number of the last error to occur, or CPLE_None (0) |
902 | | * if there are no posted errors. |
903 | | */ |
904 | | |
905 | | CPLErrorNum CPL_STDCALL CPLGetLastErrorNo() |
906 | 118 | { |
907 | 118 | CPLErrorContext *psCtx = CPLGetErrorContext(); |
908 | 118 | if (psCtx == nullptr) |
909 | 0 | return 0; |
910 | | |
911 | 118 | return psCtx->nLastErrNo; |
912 | 118 | } |
913 | | |
914 | | /********************************************************************** |
915 | | * CPLGetLastErrorType() |
916 | | **********************************************************************/ |
917 | | |
918 | | /** |
919 | | * Fetch the last error type. |
920 | | * |
921 | | * Fetches the last error type posted with CPLError(), that hasn't |
922 | | * been cleared by CPLErrorReset(). This is the error class, not the error |
923 | | * number. |
924 | | * |
925 | | * @return the error type of the last error to occur, or CE_None (0) |
926 | | * if there are no posted errors. |
927 | | */ |
928 | | |
929 | | CPLErr CPL_STDCALL CPLGetLastErrorType() |
930 | 236 | { |
931 | 236 | CPLErrorContext *psCtx = CPLGetErrorContext(); |
932 | 236 | if (psCtx == nullptr) |
933 | 0 | return CE_None; |
934 | | |
935 | 236 | return psCtx->eLastErrType; |
936 | 236 | } |
937 | | |
938 | | /********************************************************************** |
939 | | * CPLGetLastErrorMsg() |
940 | | **********************************************************************/ |
941 | | |
942 | | /** |
943 | | * Get the last error message. |
944 | | * |
945 | | * Fetches the last error message posted with CPLError(), that hasn't |
946 | | * been cleared by CPLErrorReset(). The returned pointer is to an internal |
947 | | * string that should not be altered or freed. |
948 | | * |
949 | | * @return the last error message, or an empty string ("") if there is no |
950 | | * posted error message. |
951 | | */ |
952 | | |
953 | | const char *CPL_STDCALL CPLGetLastErrorMsg() |
954 | 118 | { |
955 | 118 | CPLErrorContext *psCtx = CPLGetErrorContext(); |
956 | 118 | if (psCtx == nullptr) |
957 | 0 | return ""; |
958 | | |
959 | 118 | return psCtx->szLastErrMsg; |
960 | 118 | } |
961 | | |
962 | | /********************************************************************** |
963 | | * CPLGetErrorCounter() |
964 | | **********************************************************************/ |
965 | | |
966 | | /** |
967 | | * Get the error counter |
968 | | * |
969 | | * Fetches the number of errors emitted in the current error context, |
970 | | * since the last call to CPLErrorReset() |
971 | | * |
972 | | * @return the error counter. |
973 | | */ |
974 | | |
975 | | GUInt32 CPL_STDCALL CPLGetErrorCounter() |
976 | 0 | { |
977 | 0 | CPLErrorContext *psCtx = CPLGetErrorContext(); |
978 | 0 | if (psCtx == nullptr) |
979 | 0 | return 0; |
980 | | |
981 | 0 | return psCtx->nErrorCounter; |
982 | 0 | } |
983 | | |
984 | | /************************************************************************/ |
985 | | /* CPLDefaultErrorHandler() */ |
986 | | /************************************************************************/ |
987 | | |
988 | | static FILE *fpLog = stderr; |
989 | | static bool bLogInit = false; |
990 | | |
991 | | static FILE *CPLfopenUTF8(const char *pszFilename, const char *pszAccess) |
992 | 0 | { |
993 | 0 | FILE *f; |
994 | | #ifdef _WIN32 |
995 | | wchar_t *pwszFilename = |
996 | | CPLRecodeToWChar(pszFilename, CPL_ENC_UTF8, CPL_ENC_UCS2); |
997 | | wchar_t *pwszAccess = |
998 | | CPLRecodeToWChar(pszAccess, CPL_ENC_UTF8, CPL_ENC_UCS2); |
999 | | f = _wfopen(pwszFilename, pwszAccess); |
1000 | | VSIFree(pwszFilename); |
1001 | | VSIFree(pwszAccess); |
1002 | | #else |
1003 | 0 | f = fopen(pszFilename, pszAccess); |
1004 | 0 | #endif |
1005 | 0 | return f; |
1006 | 0 | } |
1007 | | |
1008 | | /** Default error handler. */ |
1009 | | void CPL_STDCALL CPLDefaultErrorHandler(CPLErr eErrClass, CPLErrorNum nError, |
1010 | | const char *pszErrorMsg) |
1011 | | |
1012 | 24.6k | { |
1013 | 24.6k | static int nCount = 0; |
1014 | 24.6k | static int nMaxErrors = -1; |
1015 | 24.6k | static const char *pszErrorSeparator = ":"; |
1016 | | |
1017 | 24.6k | if (eErrClass != CE_Debug) |
1018 | 22.9k | { |
1019 | 22.9k | if (nMaxErrors == -1) |
1020 | 2 | { |
1021 | 2 | nMaxErrors = |
1022 | 2 | atoi(CPLGetConfigOption("CPL_MAX_ERROR_REPORTS", "1000")); |
1023 | | // If running GDAL as a CustomBuild Command os MSBuild, "ERROR bla:" |
1024 | | // is considered as failing the job. This is rarely the intended |
1025 | | // behavior |
1026 | 2 | pszErrorSeparator = CPLGetConfigOption("CPL_ERROR_SEPARATOR", ":"); |
1027 | 2 | } |
1028 | | |
1029 | 22.9k | nCount++; |
1030 | 22.9k | if (nCount > nMaxErrors && nMaxErrors > 0) |
1031 | 20.9k | return; |
1032 | 22.9k | } |
1033 | | |
1034 | 3.69k | if (!bLogInit) |
1035 | 2 | { |
1036 | 2 | bLogInit = true; |
1037 | | |
1038 | 2 | fpLog = stderr; |
1039 | 2 | const char *pszLog = CPLGetConfigOption("CPL_LOG", nullptr); |
1040 | 2 | if (pszLog != nullptr) |
1041 | 0 | { |
1042 | 0 | const bool bAppend = |
1043 | 0 | CPLGetConfigOption("CPL_LOG_APPEND", nullptr) != nullptr; |
1044 | 0 | const char *pszAccess = bAppend ? "at" : "wt"; |
1045 | 0 | fpLog = CPLfopenUTF8(pszLog, pszAccess); |
1046 | 0 | if (fpLog == nullptr) |
1047 | 0 | fpLog = stderr; |
1048 | 0 | } |
1049 | 2 | } |
1050 | | |
1051 | 3.69k | if (eErrClass == CE_Debug) |
1052 | 1.69k | { |
1053 | 1.69k | #ifndef _WIN32 |
1054 | 1.69k | CPLErrorContext *psCtx = CPLGetErrorContext(); |
1055 | 1.69k | if (psCtx != nullptr && !IS_PREFEFINED_ERROR_CTX(psCtx) && |
1056 | 1.69k | fpLog == stderr && CPLIsInteractive(stderr)) |
1057 | 0 | { |
1058 | 0 | if (psCtx->bProgressMode) |
1059 | 0 | { |
1060 | | // Erase the content of the current line |
1061 | 0 | fprintf(stderr, "\r"); |
1062 | 0 | fprintf(stderr, "%s", pszErrorMsg); |
1063 | 0 | fflush(stderr); |
1064 | 0 | psCtx->bEmitNewlineBeforeNextDbgMsg = true; |
1065 | 0 | } |
1066 | 0 | else |
1067 | 0 | { |
1068 | 0 | if (psCtx->bEmitNewlineBeforeNextDbgMsg) |
1069 | 0 | { |
1070 | 0 | psCtx->bEmitNewlineBeforeNextDbgMsg = false; |
1071 | 0 | fprintf(fpLog, "\n"); |
1072 | 0 | } |
1073 | 0 | fprintf(fpLog, "%s\n", pszErrorMsg); |
1074 | 0 | } |
1075 | 0 | } |
1076 | 1.69k | else |
1077 | 1.69k | #endif |
1078 | 1.69k | { |
1079 | 1.69k | fprintf(fpLog, "%s\n", pszErrorMsg); |
1080 | 1.69k | } |
1081 | 1.69k | } |
1082 | 2.00k | else if (eErrClass == CE_Warning) |
1083 | 1.77k | fprintf(fpLog, "Warning %d: %s\n", nError, pszErrorMsg); |
1084 | 230 | else |
1085 | 230 | fprintf(fpLog, "ERROR %d%s %s\n", nError, pszErrorSeparator, |
1086 | 230 | pszErrorMsg); |
1087 | | |
1088 | 3.69k | if (eErrClass != CE_Debug && nMaxErrors > 0 && nCount == nMaxErrors) |
1089 | 2 | { |
1090 | 2 | fprintf(fpLog, |
1091 | 2 | "More than %d errors or warnings have been reported. " |
1092 | 2 | "No more will be reported from now.\n", |
1093 | 2 | nMaxErrors); |
1094 | 2 | } |
1095 | | |
1096 | 3.69k | fflush(fpLog); |
1097 | 3.69k | } |
1098 | | |
1099 | | /************************************************************************/ |
1100 | | /* CPLQuietErrorHandler() */ |
1101 | | /************************************************************************/ |
1102 | | |
1103 | | /** Error handler that does not do anything, except for debug messages. */ |
1104 | | void CPL_STDCALL CPLQuietErrorHandler(CPLErr eErrClass, CPLErrorNum nError, |
1105 | | const char *pszErrorMsg) |
1106 | | |
1107 | 0 | { |
1108 | 0 | if (eErrClass == CE_Debug) |
1109 | 0 | CPLDefaultErrorHandler(eErrClass, nError, pszErrorMsg); |
1110 | 0 | } |
1111 | | |
1112 | | /************************************************************************/ |
1113 | | /* CPLQuietWarningsErrorHandler() */ |
1114 | | /************************************************************************/ |
1115 | | |
1116 | | /** Error handler that ignores CE_Warning messages. */ |
1117 | | void CPL_STDCALL CPLQuietWarningsErrorHandler(CPLErr eErrClass, |
1118 | | CPLErrorNum nError, |
1119 | | const char *pszErrorMsg) |
1120 | | |
1121 | 0 | { |
1122 | 0 | if (eErrClass != CE_Warning) |
1123 | 0 | CPLDefaultErrorHandler(eErrClass, nError, pszErrorMsg); |
1124 | 0 | } |
1125 | | |
1126 | | /************************************************************************/ |
1127 | | /* CPLLoggingErrorHandler() */ |
1128 | | /************************************************************************/ |
1129 | | |
1130 | | /** Error handler that logs into the file defined by the CPL_LOG configuration |
1131 | | * option, or stderr otherwise. |
1132 | | */ |
1133 | | void CPL_STDCALL CPLLoggingErrorHandler(CPLErr eErrClass, CPLErrorNum nError, |
1134 | | const char *pszErrorMsg) |
1135 | | |
1136 | 0 | { |
1137 | 0 | if (!bLogInit) |
1138 | 0 | { |
1139 | 0 | bLogInit = true; |
1140 | |
|
1141 | 0 | CPLSetConfigOption("CPL_TIMESTAMP", "ON"); |
1142 | |
|
1143 | 0 | const char *cpl_log = CPLGetConfigOption("CPL_LOG", nullptr); |
1144 | |
|
1145 | 0 | fpLog = stderr; |
1146 | 0 | if (cpl_log != nullptr && EQUAL(cpl_log, "OFF")) |
1147 | 0 | { |
1148 | 0 | fpLog = nullptr; |
1149 | 0 | } |
1150 | 0 | else if (cpl_log != nullptr) |
1151 | 0 | { |
1152 | 0 | size_t nPathLen = strlen(cpl_log) + 20; |
1153 | 0 | char *pszPath = static_cast<char *>(CPLMalloc(nPathLen)); |
1154 | 0 | strcpy(pszPath, cpl_log); |
1155 | |
|
1156 | 0 | int i = 0; |
1157 | 0 | while ((fpLog = CPLfopenUTF8(pszPath, "rt")) != nullptr) |
1158 | 0 | { |
1159 | 0 | fclose(fpLog); |
1160 | | |
1161 | | // Generate sequenced log file names, inserting # before ext. |
1162 | 0 | if (strrchr(cpl_log, '.') == nullptr) |
1163 | 0 | { |
1164 | 0 | snprintf(pszPath, nPathLen, "%s_%d%s", cpl_log, i++, |
1165 | 0 | ".log"); |
1166 | 0 | } |
1167 | 0 | else |
1168 | 0 | { |
1169 | 0 | size_t pos = 0; |
1170 | 0 | char *cpl_log_base = CPLStrdup(cpl_log); |
1171 | 0 | pos = strcspn(cpl_log_base, "."); |
1172 | 0 | if (pos > 0) |
1173 | 0 | { |
1174 | 0 | cpl_log_base[pos] = '\0'; |
1175 | 0 | } |
1176 | 0 | snprintf(pszPath, nPathLen, "%s_%d%s", cpl_log_base, i++, |
1177 | 0 | ".log"); |
1178 | 0 | CPLFree(cpl_log_base); |
1179 | 0 | } |
1180 | 0 | } |
1181 | |
|
1182 | 0 | fpLog = CPLfopenUTF8(pszPath, "wt"); |
1183 | 0 | CPLFree(pszPath); |
1184 | 0 | } |
1185 | 0 | } |
1186 | |
|
1187 | 0 | if (fpLog == nullptr) |
1188 | 0 | return; |
1189 | | |
1190 | 0 | if (eErrClass == CE_Debug) |
1191 | 0 | fprintf(fpLog, "%s\n", pszErrorMsg); |
1192 | 0 | else if (eErrClass == CE_Warning) |
1193 | 0 | fprintf(fpLog, "Warning %d: %s\n", nError, pszErrorMsg); |
1194 | 0 | else |
1195 | 0 | fprintf(fpLog, "ERROR %d: %s\n", nError, pszErrorMsg); |
1196 | |
|
1197 | 0 | fflush(fpLog); |
1198 | 0 | } |
1199 | | |
1200 | | /********************************************************************** |
1201 | | * CPLTurnFailureIntoWarning() * |
1202 | | **********************************************************************/ |
1203 | | |
1204 | | /** Whether failures should be turned into warnings. |
1205 | | */ |
1206 | | void CPLTurnFailureIntoWarning(int bOn) |
1207 | 0 | { |
1208 | 0 | CPLErrorContext *psCtx = CPLGetErrorContext(); |
1209 | 0 | if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx)) |
1210 | 0 | { |
1211 | 0 | fprintf(stderr, "CPLTurnFailureIntoWarning() failed.\n"); |
1212 | 0 | return; |
1213 | 0 | } |
1214 | 0 | psCtx->nFailureIntoWarning += (bOn) ? 1 : -1; |
1215 | 0 | if (psCtx->nFailureIntoWarning < 0) |
1216 | 0 | { |
1217 | 0 | CPLDebug("CPL", "Wrong nesting of CPLTurnFailureIntoWarning(TRUE) / " |
1218 | 0 | "CPLTurnFailureIntoWarning(FALSE)"); |
1219 | 0 | } |
1220 | 0 | } |
1221 | | |
1222 | | /********************************************************************** |
1223 | | * CPLSetErrorHandlerEx() * |
1224 | | **********************************************************************/ |
1225 | | |
1226 | | /** |
1227 | | * Install custom error handle with user's data. This method is |
1228 | | * essentially CPLSetErrorHandler with an added pointer to pUserData. |
1229 | | * The pUserData is not returned in the CPLErrorHandler, however, and |
1230 | | * must be fetched via CPLGetErrorHandlerUserData. |
1231 | | * |
1232 | | * @param pfnErrorHandlerNew new error handler function. |
1233 | | * @param pUserData User data to carry along with the error context. |
1234 | | * @return returns the previously installed error handler. |
1235 | | */ |
1236 | | |
1237 | | CPLErrorHandler CPL_STDCALL |
1238 | | CPLSetErrorHandlerEx(CPLErrorHandler pfnErrorHandlerNew, void *pUserData) |
1239 | 0 | { |
1240 | 0 | CPLErrorContext *psCtx = CPLGetErrorContext(); |
1241 | 0 | if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx)) |
1242 | 0 | { |
1243 | 0 | fprintf(stderr, "CPLSetErrorHandlerEx() failed.\n"); |
1244 | 0 | return nullptr; |
1245 | 0 | } |
1246 | | |
1247 | 0 | if (psCtx->psHandlerStack != nullptr) |
1248 | 0 | { |
1249 | 0 | CPLDebug("CPL", "CPLSetErrorHandler() called with an error handler on " |
1250 | 0 | "the local stack. New error handler will not be used " |
1251 | 0 | "immediately."); |
1252 | 0 | } |
1253 | |
|
1254 | 0 | CPLErrorHandler pfnOldHandler = nullptr; |
1255 | 0 | { |
1256 | 0 | CPLMutexHolderD(&hErrorMutex); |
1257 | |
|
1258 | 0 | pfnOldHandler = pfnErrorHandler; |
1259 | |
|
1260 | 0 | pfnErrorHandler = pfnErrorHandlerNew; |
1261 | |
|
1262 | 0 | pErrorHandlerUserData = pUserData; |
1263 | 0 | } |
1264 | |
|
1265 | 0 | return pfnOldHandler; |
1266 | 0 | } |
1267 | | |
1268 | | /********************************************************************** |
1269 | | * CPLSetErrorHandler() * |
1270 | | **********************************************************************/ |
1271 | | |
1272 | | /** |
1273 | | * Install custom error handler. |
1274 | | * |
1275 | | * Allow the library's user to specify an error handler function. |
1276 | | * A valid error handler is a C function with the following prototype: |
1277 | | * |
1278 | | * \code{.cpp} |
1279 | | * void MyErrorHandler(CPLErr eErrClass, int err_no, const char *msg) |
1280 | | * \endcode |
1281 | | * |
1282 | | * Pass NULL to come back to the default behavior. The default behavior |
1283 | | * (CPLDefaultErrorHandler()) is to write the message to stderr. |
1284 | | * |
1285 | | * The msg will be a partially formatted error message not containing the |
1286 | | * "ERROR %d:" portion emitted by the default handler. Message formatting |
1287 | | * is handled by CPLError() before calling the handler. If the error |
1288 | | * handler function is passed a CE_Fatal class error and returns, then |
1289 | | * CPLError() will call abort(). Applications wanting to interrupt this |
1290 | | * fatal behavior will have to use longjmp(), or a C++ exception to |
1291 | | * indirectly exit the function. |
1292 | | * |
1293 | | * Another standard error handler is CPLQuietErrorHandler() which doesn't |
1294 | | * make any attempt to report the passed error or warning messages but |
1295 | | * will process debug messages via CPLDefaultErrorHandler. |
1296 | | * |
1297 | | * Note that error handlers set with CPLSetErrorHandler() apply to all |
1298 | | * threads in an application, while error handlers set with CPLPushErrorHandler |
1299 | | * are thread-local. However, any error handlers pushed with |
1300 | | * CPLPushErrorHandler (and not removed with CPLPopErrorHandler) take |
1301 | | * precedence over the global error handlers set with CPLSetErrorHandler(). |
1302 | | * Generally speaking CPLSetErrorHandler() would be used to set a desired |
1303 | | * global error handler, while CPLPushErrorHandler() would be used to install |
1304 | | * a temporary local error handler, such as CPLQuietErrorHandler() to suppress |
1305 | | * error reporting in a limited segment of code. |
1306 | | * |
1307 | | * @param pfnErrorHandlerNew new error handler function. |
1308 | | * @return returns the previously installed error handler. |
1309 | | */ |
1310 | | CPLErrorHandler CPL_STDCALL |
1311 | | CPLSetErrorHandler(CPLErrorHandler pfnErrorHandlerNew) |
1312 | 0 | { |
1313 | 0 | return CPLSetErrorHandlerEx(pfnErrorHandlerNew, nullptr); |
1314 | 0 | } |
1315 | | |
1316 | | /************************************************************************/ |
1317 | | /* CPLPushErrorHandler() */ |
1318 | | /************************************************************************/ |
1319 | | |
1320 | | /** |
1321 | | * Push a new CPLError handler. |
1322 | | * |
1323 | | * This pushes a new error handler on the thread-local error handler |
1324 | | * stack. This handler will be used until removed with CPLPopErrorHandler(). |
1325 | | * |
1326 | | * The CPLSetErrorHandler() docs have further information on how |
1327 | | * CPLError handlers work. |
1328 | | * |
1329 | | * @param pfnErrorHandlerNew new error handler function. |
1330 | | */ |
1331 | | |
1332 | | void CPL_STDCALL CPLPushErrorHandler(CPLErrorHandler pfnErrorHandlerNew) |
1333 | | |
1334 | 0 | { |
1335 | 0 | CPLPushErrorHandlerEx(pfnErrorHandlerNew, nullptr); |
1336 | 0 | } |
1337 | | |
1338 | | /************************************************************************/ |
1339 | | /* CPLPushErrorHandlerEx() */ |
1340 | | /************************************************************************/ |
1341 | | |
1342 | | /** |
1343 | | * Push a new CPLError handler with user data on the error context. |
1344 | | * |
1345 | | * This pushes a new error handler on the thread-local error handler |
1346 | | * stack. This handler will be used until removed with CPLPopErrorHandler(). |
1347 | | * Obtain the user data back by using CPLGetErrorContext(). |
1348 | | * |
1349 | | * The CPLSetErrorHandler() docs have further information on how |
1350 | | * CPLError handlers work. |
1351 | | * |
1352 | | * @param pfnErrorHandlerNew new error handler function. |
1353 | | * @param pUserData User data to put on the error context. |
1354 | | */ |
1355 | | void CPL_STDCALL CPLPushErrorHandlerEx(CPLErrorHandler pfnErrorHandlerNew, |
1356 | | void *pUserData) |
1357 | | |
1358 | 0 | { |
1359 | 0 | CPLErrorContext *psCtx = CPLGetErrorContext(); |
1360 | |
|
1361 | 0 | if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx)) |
1362 | 0 | { |
1363 | 0 | fprintf(stderr, "CPLPushErrorHandlerEx() failed.\n"); |
1364 | 0 | return; |
1365 | 0 | } |
1366 | | |
1367 | 0 | CPLErrorHandlerNode *psNode = static_cast<CPLErrorHandlerNode *>( |
1368 | 0 | CPLMalloc(sizeof(CPLErrorHandlerNode))); |
1369 | 0 | psNode->psNext = psCtx->psHandlerStack; |
1370 | 0 | psNode->pfnHandler = pfnErrorHandlerNew; |
1371 | 0 | psNode->pUserData = pUserData; |
1372 | 0 | psNode->bCatchDebug = true; |
1373 | 0 | psCtx->psHandlerStack = psNode; |
1374 | 0 | } |
1375 | | |
1376 | | /************************************************************************/ |
1377 | | /* CPLPopErrorHandler() */ |
1378 | | /************************************************************************/ |
1379 | | |
1380 | | /** |
1381 | | * Pop error handler off stack. |
1382 | | * |
1383 | | * Discards the current error handler on the error handler stack, and restores |
1384 | | * the one in use before the last CPLPushErrorHandler() call. This method |
1385 | | * has no effect if there are no error handlers on the current threads error |
1386 | | * handler stack. |
1387 | | */ |
1388 | | |
1389 | | void CPL_STDCALL CPLPopErrorHandler() |
1390 | | |
1391 | 0 | { |
1392 | 0 | CPLErrorContext *psCtx = CPLGetErrorContext(); |
1393 | |
|
1394 | 0 | if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx)) |
1395 | 0 | { |
1396 | 0 | fprintf(stderr, "CPLPopErrorHandler() failed.\n"); |
1397 | 0 | return; |
1398 | 0 | } |
1399 | | |
1400 | 0 | if (psCtx->psHandlerStack != nullptr) |
1401 | 0 | { |
1402 | 0 | CPLErrorHandlerNode *psNode = psCtx->psHandlerStack; |
1403 | |
|
1404 | 0 | psCtx->psHandlerStack = psNode->psNext; |
1405 | 0 | VSIFree(psNode); |
1406 | 0 | } |
1407 | 0 | } |
1408 | | |
1409 | | /************************************************************************/ |
1410 | | /* CPLCallPreviousHandler() */ |
1411 | | /************************************************************************/ |
1412 | | |
1413 | | /** |
1414 | | * Call the previously installed error handler in the error handler stack. |
1415 | | * |
1416 | | * Only to be used by a custom error handler that wants to forward events to |
1417 | | * the previous error handler. |
1418 | | * |
1419 | | * @since GDAL 3.8 |
1420 | | */ |
1421 | | |
1422 | | void CPLCallPreviousHandler(CPLErr eErrClass, CPLErrorNum err_no, |
1423 | | const char *pszMsg) |
1424 | 0 | { |
1425 | 0 | CPLErrorContext *psCtx = CPLGetErrorContext(); |
1426 | |
|
1427 | 0 | if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx)) |
1428 | 0 | { |
1429 | 0 | fprintf(stderr, "CPLCallPreviousHandler() failed.\n"); |
1430 | 0 | return; |
1431 | 0 | } |
1432 | | |
1433 | 0 | if (psCtx->psHandlerStack != nullptr) |
1434 | 0 | { |
1435 | 0 | CPLErrorHandlerNode *psCurNode = psCtx->psHandlerStack; |
1436 | 0 | psCtx->psHandlerStack = psCurNode->psNext; |
1437 | 0 | if (psCtx->psHandlerStack) |
1438 | 0 | { |
1439 | 0 | CPLErrorHandlerNode *psNewCurNode = psCtx->psHandlerStack; |
1440 | 0 | psCtx->psHandlerStack->pfnHandler(eErrClass, err_no, pszMsg); |
1441 | 0 | if (psNewCurNode != psCtx->psHandlerStack) |
1442 | 0 | { |
1443 | 0 | fprintf(stderr, "CPLCallPreviousHandler() has detected that a " |
1444 | 0 | "previous error handler messed up with the " |
1445 | 0 | "error stack. Chaos guaranteed!\n"); |
1446 | 0 | } |
1447 | 0 | } |
1448 | 0 | else |
1449 | 0 | CPLDefaultErrorHandler(eErrClass, err_no, pszMsg); |
1450 | 0 | psCtx->psHandlerStack = psCurNode; |
1451 | 0 | } |
1452 | 0 | else |
1453 | 0 | { |
1454 | 0 | CPLDefaultErrorHandler(eErrClass, err_no, pszMsg); |
1455 | 0 | } |
1456 | 0 | } |
1457 | | |
1458 | | /************************************************************************/ |
1459 | | /* CPLSetCurrentErrorHandlerCatchDebug() */ |
1460 | | /************************************************************************/ |
1461 | | |
1462 | | /** |
1463 | | * Set if the current error handler should intercept debug messages, or if |
1464 | | * they should be processed by the previous handler. |
1465 | | * |
1466 | | * By default when installing a custom error handler, this one intercepts |
1467 | | * debug messages. In some cases, this might not be desirable and the user |
1468 | | * would prefer that the previous installed handler (or the default one if no |
1469 | | * previous installed handler exists in the stack) deal with it. In which |
1470 | | * case, this function should be called with bCatchDebug = FALSE. |
1471 | | * |
1472 | | * @param bCatchDebug FALSE if the current error handler should not intercept |
1473 | | * debug messages |
1474 | | */ |
1475 | | |
1476 | | void CPL_STDCALL CPLSetCurrentErrorHandlerCatchDebug(int bCatchDebug) |
1477 | 0 | { |
1478 | 0 | CPLErrorContext *psCtx = CPLGetErrorContext(); |
1479 | |
|
1480 | 0 | if (psCtx == nullptr || IS_PREFEFINED_ERROR_CTX(psCtx)) |
1481 | 0 | { |
1482 | 0 | fprintf(stderr, "CPLSetCurrentErrorHandlerCatchDebug() failed.\n"); |
1483 | 0 | return; |
1484 | 0 | } |
1485 | | |
1486 | 0 | if (psCtx->psHandlerStack != nullptr) |
1487 | 0 | psCtx->psHandlerStack->bCatchDebug = CPL_TO_BOOL(bCatchDebug); |
1488 | 0 | else |
1489 | 0 | gbCatchDebug = CPL_TO_BOOL(bCatchDebug); |
1490 | 0 | } |
1491 | | |
1492 | | /************************************************************************/ |
1493 | | /* _CPLAssert() */ |
1494 | | /* */ |
1495 | | /* This function is called only when an assertion fails. */ |
1496 | | /************************************************************************/ |
1497 | | |
1498 | | /** |
1499 | | * Report failure of a logical assertion. |
1500 | | * |
1501 | | * Applications would normally use the CPLAssert() macro which expands |
1502 | | * into code calling _CPLAssert() only if the condition fails. _CPLAssert() |
1503 | | * will generate a CE_Fatal error call to CPLError(), indicating the file |
1504 | | * name, and line number of the failed assertion, as well as containing |
1505 | | * the assertion itself. |
1506 | | * |
1507 | | * There is no reason for application code to call _CPLAssert() directly. |
1508 | | */ |
1509 | | |
1510 | | void CPL_STDCALL _CPLAssert(const char *pszExpression, const char *pszFile, |
1511 | | int iLine) |
1512 | | |
1513 | 0 | { |
1514 | 0 | CPLError(CE_Fatal, CPLE_AssertionFailed, |
1515 | 0 | "Assertion `%s' failed " |
1516 | 0 | "in file `%s', line %d", |
1517 | 0 | pszExpression, pszFile, iLine); |
1518 | | |
1519 | | // Just to please compiler so it is aware the function does not return. |
1520 | 0 | abort(); |
1521 | 0 | } |
1522 | | |
1523 | | /************************************************************************/ |
1524 | | /* CPLCleanupErrorMutex() */ |
1525 | | /************************************************************************/ |
1526 | | |
1527 | | void CPLCleanupErrorMutex() |
1528 | 0 | { |
1529 | 0 | if (hErrorMutex != nullptr) |
1530 | 0 | { |
1531 | 0 | CPLDestroyMutex(hErrorMutex); |
1532 | 0 | hErrorMutex = nullptr; |
1533 | 0 | } |
1534 | 0 | if (fpLog != nullptr && fpLog != stderr) |
1535 | 0 | { |
1536 | 0 | fclose(fpLog); |
1537 | 0 | fpLog = nullptr; |
1538 | 0 | bLogInit = false; |
1539 | 0 | } |
1540 | 0 | } |
1541 | | |
1542 | | bool CPLIsDefaultErrorHandlerAndCatchDebug() |
1543 | 0 | { |
1544 | 0 | CPLErrorContext *psCtx = CPLGetErrorContext(); |
1545 | 0 | return (psCtx == nullptr || psCtx->psHandlerStack == nullptr) && |
1546 | 0 | gbCatchDebug && pfnErrorHandler == CPLDefaultErrorHandler; |
1547 | 0 | } |
1548 | | |
1549 | | /************************************************************************/ |
1550 | | /* CPLErrorStateBackuper::CPLErrorStateBackuper() */ |
1551 | | /************************************************************************/ |
1552 | | |
1553 | | CPLErrorStateBackuper::CPLErrorStateBackuper(CPLErrorHandler hHandler) |
1554 | 0 | : m_nLastErrorNum(CPLGetLastErrorNo()), |
1555 | 0 | m_nLastErrorType(CPLGetLastErrorType()), |
1556 | 0 | m_osLastErrorMsg(CPLGetLastErrorMsg()), |
1557 | 0 | m_nLastErrorCounter(CPLGetErrorCounter()), |
1558 | | m_poErrorHandlerPusher( |
1559 | 0 | hHandler ? std::make_unique<CPLErrorHandlerPusher>(hHandler) |
1560 | 0 | : nullptr) |
1561 | 0 | { |
1562 | 0 | } |
1563 | | |
1564 | | /************************************************************************/ |
1565 | | /* CPLErrorStateBackuper::~CPLErrorStateBackuper() */ |
1566 | | /************************************************************************/ |
1567 | | |
1568 | | CPLErrorStateBackuper::~CPLErrorStateBackuper() |
1569 | 0 | { |
1570 | 0 | CPLErrorSetState(m_nLastErrorType, m_nLastErrorNum, |
1571 | 0 | m_osLastErrorMsg.c_str(), &m_nLastErrorCounter); |
1572 | 0 | } |
1573 | | |
1574 | | /*! @cond Doxygen_Suppress */ |
1575 | | |
1576 | | /************************************************************************/ |
1577 | | /* CPLErrorAccumulator::Context::~Context() */ |
1578 | | /************************************************************************/ |
1579 | | |
1580 | | CPLErrorAccumulator::Context::~Context() |
1581 | 0 | { |
1582 | 0 | CPLPopErrorHandler(); |
1583 | 0 | } |
1584 | | |
1585 | | /************************************************************************/ |
1586 | | /* CPLErrorAccumulator::InstallForCurrentScope() */ |
1587 | | /************************************************************************/ |
1588 | | |
1589 | | CPLErrorAccumulator::Context CPLErrorAccumulator::InstallForCurrentScope() |
1590 | 0 | { |
1591 | 0 | CPLPushErrorHandlerEx(CPLErrorAccumulator::Accumulator, this); |
1592 | 0 | return CPLErrorAccumulator::Context(); |
1593 | 0 | } |
1594 | | |
1595 | | /************************************************************************/ |
1596 | | /* CPLErrorAccumulator::ReplayErrors() */ |
1597 | | /************************************************************************/ |
1598 | | |
1599 | | void CPLErrorAccumulator::ReplayErrors() |
1600 | 0 | { |
1601 | 0 | std::lock_guard oLock(mutex); |
1602 | 0 | for (const auto &err : errors) |
1603 | 0 | { |
1604 | 0 | CPLError(err.type, err.no, "%s", err.msg.c_str()); |
1605 | 0 | } |
1606 | 0 | } |
1607 | | |
1608 | | /************************************************************************/ |
1609 | | /* CPLErrorAccumulator::Accumulator() */ |
1610 | | /************************************************************************/ |
1611 | | |
1612 | | /* static */ void CPL_STDCALL CPLErrorAccumulator::Accumulator(CPLErr eErr, |
1613 | | CPLErrorNum no, |
1614 | | const char *msg) |
1615 | 0 | { |
1616 | 0 | if (eErr != CE_Debug) |
1617 | 0 | { |
1618 | 0 | CPLErrorAccumulator *pThis = |
1619 | 0 | static_cast<CPLErrorAccumulator *>(CPLGetErrorHandlerUserData()); |
1620 | 0 | std::lock_guard oLock(pThis->mutex); |
1621 | 0 | pThis->errors.push_back( |
1622 | 0 | CPLErrorHandlerAccumulatorStruct(eErr, no, msg)); |
1623 | 0 | } |
1624 | 0 | } |
1625 | | |
1626 | | /*! @endcond */ |