[1] | 1 | /* $Id: assert.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
|
---|
| 2 | /** @file
|
---|
[25528] | 3 | * IPRT - Assertions, common code.
|
---|
[1] | 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[98103] | 7 | * Copyright (C) 2006-2023 Oracle and/or its affiliates.
|
---|
[1] | 8 | *
|
---|
[96407] | 9 | * This file is part of VirtualBox base platform packages, as
|
---|
| 10 | * available from https://www.virtualbox.org.
|
---|
[5999] | 11 | *
|
---|
[96407] | 12 | * This program is free software; you can redistribute it and/or
|
---|
| 13 | * modify it under the terms of the GNU General Public License
|
---|
| 14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
| 15 | * License.
|
---|
| 16 | *
|
---|
| 17 | * This program is distributed in the hope that it will be useful, but
|
---|
| 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
| 20 | * General Public License for more details.
|
---|
| 21 | *
|
---|
| 22 | * You should have received a copy of the GNU General Public License
|
---|
| 23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
| 24 | *
|
---|
[5999] | 25 | * The contents of this file may alternatively be used under the terms
|
---|
| 26 | * of the Common Development and Distribution License Version 1.0
|
---|
[96407] | 27 | * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
|
---|
| 28 | * in the VirtualBox distribution, in which case the provisions of the
|
---|
[5999] | 29 | * CDDL are applicable instead of those of the GPL.
|
---|
| 30 | *
|
---|
| 31 | * You may elect to license modified versions of this file under the
|
---|
| 32 | * terms and conditions of either the GPL or the CDDL or both.
|
---|
[96407] | 33 | *
|
---|
| 34 | * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
|
---|
[1] | 35 | */
|
---|
| 36 |
|
---|
| 37 |
|
---|
[57358] | 38 | /*********************************************************************************************************************************
|
---|
| 39 | * Header Files *
|
---|
| 40 | *********************************************************************************************************************************/
|
---|
[1] | 41 | #include <iprt/assert.h>
|
---|
[21337] | 42 | #include "internal/iprt.h"
|
---|
| 43 |
|
---|
[25518] | 44 | #include <iprt/asm.h>
|
---|
[73762] | 45 | #ifdef IPRT_WITH_ASSERT_STACK
|
---|
| 46 | # ifndef IN_RING3
|
---|
| 47 | # error "IPRT_WITH_ASSERT_STACK is only for ring-3 at present."
|
---|
| 48 | # endif
|
---|
| 49 | # include <iprt/dbg.h>
|
---|
| 50 | #endif
|
---|
[76452] | 51 | #include <iprt/errcore.h>
|
---|
[1] | 52 | #include <iprt/log.h>
|
---|
| 53 | #include <iprt/string.h>
|
---|
| 54 | #include <iprt/stdarg.h>
|
---|
| 55 | #ifdef IN_RING3
|
---|
[74344] | 56 | # include <iprt/env.h>
|
---|
[95809] | 57 | # ifndef IPRT_NO_CRT
|
---|
| 58 | # include <stdio.h>
|
---|
| 59 | # endif
|
---|
[74344] | 60 | # ifdef RT_OS_WINDOWS
|
---|
| 61 | # include <iprt/win/windows.h>
|
---|
[96476] | 62 | # include "../../r3/win/internal-r3-win.h"
|
---|
[74344] | 63 | # endif
|
---|
[1] | 64 | #endif
|
---|
[25528] | 65 | #include "internal/assert.h"
|
---|
[1] | 66 |
|
---|
| 67 |
|
---|
[57358] | 68 | /*********************************************************************************************************************************
|
---|
| 69 | * Global Variables *
|
---|
| 70 | *********************************************************************************************************************************/
|
---|
[73762] | 71 | /** The last assertion message, 1st part. */
|
---|
[25528] | 72 | RTDATADECL(char) g_szRTAssertMsg1[1024];
|
---|
| 73 | RT_EXPORT_SYMBOL(g_szRTAssertMsg1);
|
---|
[73762] | 74 | /** The last assertion message, 2nd part. */
|
---|
[25536] | 75 | RTDATADECL(char) g_szRTAssertMsg2[4096];
|
---|
[25528] | 76 | RT_EXPORT_SYMBOL(g_szRTAssertMsg2);
|
---|
[73762] | 77 | #ifdef IPRT_WITH_ASSERT_STACK
|
---|
| 78 | /** The last assertion message, stack part. */
|
---|
| 79 | RTDATADECL(char) g_szRTAssertStack[4096];
|
---|
| 80 | RT_EXPORT_SYMBOL(g_szRTAssertStack);
|
---|
| 81 | #endif
|
---|
[25536] | 82 | /** The length of the g_szRTAssertMsg2 content.
|
---|
| 83 | * @remarks Race. */
|
---|
| 84 | static uint32_t volatile g_cchRTAssertMsg2;
|
---|
[73762] | 85 | /** The last assertion message, expression. */
|
---|
[25528] | 86 | RTDATADECL(const char * volatile) g_pszRTAssertExpr;
|
---|
| 87 | RT_EXPORT_SYMBOL(g_pszRTAssertExpr);
|
---|
[73762] | 88 | /** The last assertion message, function name. */
|
---|
[25528] | 89 | RTDATADECL(const char * volatile) g_pszRTAssertFunction;
|
---|
| 90 | RT_EXPORT_SYMBOL(g_pszRTAssertFunction);
|
---|
[73762] | 91 | /** The last assertion message, file name. */
|
---|
[25528] | 92 | RTDATADECL(const char * volatile) g_pszRTAssertFile;
|
---|
| 93 | RT_EXPORT_SYMBOL(g_pszRTAssertFile);
|
---|
[73762] | 94 | /** The last assertion message, line number. */
|
---|
[25528] | 95 | RTDATADECL(uint32_t volatile) g_u32RTAssertLine;
|
---|
| 96 | RT_EXPORT_SYMBOL(g_u32RTAssertLine);
|
---|
| 97 |
|
---|
| 98 |
|
---|
[25518] | 99 | /** Set if assertions are quiet. */
|
---|
[25528] | 100 | static bool volatile g_fQuiet = false;
|
---|
[25518] | 101 | /** Set if assertions may panic. */
|
---|
[64286] | 102 | static bool volatile g_fMayPanic = true;
|
---|
[25518] | 103 |
|
---|
| 104 |
|
---|
| 105 | RTDECL(bool) RTAssertSetQuiet(bool fQuiet)
|
---|
| 106 | {
|
---|
| 107 | return ASMAtomicXchgBool(&g_fQuiet, fQuiet);
|
---|
| 108 | }
|
---|
[25528] | 109 | RT_EXPORT_SYMBOL(RTAssertSetQuiet);
|
---|
[25518] | 110 |
|
---|
| 111 |
|
---|
| 112 | RTDECL(bool) RTAssertAreQuiet(void)
|
---|
| 113 | {
|
---|
| 114 | return ASMAtomicUoReadBool(&g_fQuiet);
|
---|
| 115 | }
|
---|
[25528] | 116 | RT_EXPORT_SYMBOL(RTAssertAreQuiet);
|
---|
[25518] | 117 |
|
---|
| 118 |
|
---|
| 119 | RTDECL(bool) RTAssertSetMayPanic(bool fMayPanic)
|
---|
| 120 | {
|
---|
| 121 | return ASMAtomicXchgBool(&g_fMayPanic, fMayPanic);
|
---|
| 122 | }
|
---|
[25528] | 123 | RT_EXPORT_SYMBOL(RTAssertSetMayPanic);
|
---|
[25518] | 124 |
|
---|
| 125 |
|
---|
| 126 | RTDECL(bool) RTAssertMayPanic(void)
|
---|
| 127 | {
|
---|
| 128 | return ASMAtomicUoReadBool(&g_fMayPanic);
|
---|
| 129 | }
|
---|
[25528] | 130 | RT_EXPORT_SYMBOL(RTAssertMayPanic);
|
---|
[25518] | 131 |
|
---|
| 132 |
|
---|
[25528] | 133 | RTDECL(void) RTAssertMsg1(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction)
|
---|
| 134 | {
|
---|
| 135 | /*
|
---|
| 136 | * Fill in the globals.
|
---|
| 137 | */
|
---|
[30111] | 138 | ASMAtomicUoWritePtr(&g_pszRTAssertExpr, pszExpr);
|
---|
| 139 | ASMAtomicUoWritePtr(&g_pszRTAssertFile, pszFile);
|
---|
| 140 | ASMAtomicUoWritePtr(&g_pszRTAssertFunction, pszFunction);
|
---|
[25528] | 141 | ASMAtomicUoWriteU32(&g_u32RTAssertLine, uLine);
|
---|
| 142 | RTStrPrintf(g_szRTAssertMsg1, sizeof(g_szRTAssertMsg1),
|
---|
| 143 | "\n!!Assertion Failed!!\n"
|
---|
| 144 | "Expression: %s\n"
|
---|
| 145 | "Location : %s(%d) %s\n",
|
---|
| 146 | pszExpr, pszFile, uLine, pszFunction);
|
---|
[1] | 147 |
|
---|
[25528] | 148 | /*
|
---|
| 149 | * If not quiet, make noise.
|
---|
| 150 | */
|
---|
[25518] | 151 | if (!RTAssertAreQuiet())
|
---|
| 152 | {
|
---|
[37233] | 153 | RTERRVARS SavedErrVars;
|
---|
| 154 | RTErrVarsSave(&SavedErrVars);
|
---|
| 155 |
|
---|
[73762] | 156 | #ifdef IPRT_WITH_ASSERT_STACK
|
---|
| 157 | /* The stack dump. */
|
---|
[75235] | 158 | static volatile bool s_fDumpingStackAlready = false; /* for simple recursion prevention */
|
---|
[74344] | 159 | char szStack[sizeof(g_szRTAssertStack)];
|
---|
| 160 | size_t cchStack = 0;
|
---|
| 161 | # if defined(IN_RING3) && defined(RT_OS_WINDOWS) /** @todo make this stack on/off thing more modular. */
|
---|
[96476] | 162 | bool fStack = (!g_pfnIsDebuggerPresent || !g_pfnIsDebuggerPresent()) && !RTEnvExist("IPRT_ASSERT_NO_STACK");
|
---|
[74344] | 163 | # elif defined(IN_RING3)
|
---|
| 164 | bool fStack = !RTEnvExist("IPRT_ASSERT_NO_STACK");
|
---|
| 165 | # else
|
---|
| 166 | bool fStack = true;
|
---|
| 167 | # endif
|
---|
[75235] | 168 | szStack[0] = '\0';
|
---|
| 169 | if (fStack && !s_fDumpingStackAlready)
|
---|
| 170 | {
|
---|
| 171 | s_fDumpingStackAlready = true;
|
---|
[74344] | 172 | cchStack = RTDbgStackDumpSelf(szStack, sizeof(szStack), 0);
|
---|
[75235] | 173 | s_fDumpingStackAlready = false;
|
---|
| 174 | }
|
---|
[73762] | 175 | memcpy(g_szRTAssertStack, szStack, cchStack + 1);
|
---|
| 176 | #endif
|
---|
| 177 |
|
---|
[25528] | 178 | #ifdef IN_RING0
|
---|
| 179 | # ifdef IN_GUEST_R0
|
---|
| 180 | RTLogBackdoorPrintf("\n!!Assertion Failed!!\n"
|
---|
| 181 | "Expression: %s\n"
|
---|
| 182 | "Location : %s(%d) %s\n",
|
---|
| 183 | pszExpr, pszFile, uLine, pszFunction);
|
---|
| 184 | # endif
|
---|
| 185 | /** @todo fully integrate this with the logger... play safe a bit for now. */
|
---|
| 186 | rtR0AssertNativeMsg1(pszExpr, uLine, pszFile, pszFunction);
|
---|
| 187 |
|
---|
| 188 | #else /* !IN_RING0 */
|
---|
[25518] | 189 |
|
---|
[1] | 190 |
|
---|
[96448] | 191 | # if defined(IN_RING3) && (defined(IN_RT_STATIC) || defined(IPRT_NO_CRT)) /* ugly */
|
---|
| 192 | if (g_pfnRTLogAssert)
|
---|
| 193 | g_pfnRTLogAssert(
|
---|
| 194 | # else
|
---|
| 195 | RTLogAssert(
|
---|
[25528] | 196 | # endif
|
---|
[96448] | 197 | "\n!!Assertion Failed!!\n"
|
---|
| 198 | "Expression: %s\n"
|
---|
| 199 | "Location : %s(%d) %s\n"
|
---|
[73762] | 200 | # ifdef IPRT_WITH_ASSERT_STACK
|
---|
[96448] | 201 | "Stack :\n%s\n"
|
---|
[73762] | 202 | # endif
|
---|
[96448] | 203 | , pszExpr, pszFile, uLine, pszFunction
|
---|
| 204 | # ifdef IPRT_WITH_ASSERT_STACK
|
---|
| 205 | , szStack
|
---|
[25528] | 206 | # endif
|
---|
[96448] | 207 | );
|
---|
[1] | 208 |
|
---|
[25528] | 209 | # ifdef IN_RING3
|
---|
[25518] | 210 | /* print to stderr, helps user and gdb debugging. */
|
---|
[95809] | 211 | # ifndef IPRT_NO_CRT
|
---|
[25518] | 212 | fprintf(stderr,
|
---|
| 213 | "\n!!Assertion Failed!!\n"
|
---|
| 214 | "Expression: %s\n"
|
---|
| 215 | "Location : %s(%d) %s\n",
|
---|
[90803] | 216 | RT_VALID_PTR(pszExpr) ? pszExpr : "<none>",
|
---|
| 217 | RT_VALID_PTR(pszFile) ? pszFile : "<none>",
|
---|
[25518] | 218 | uLine,
|
---|
[90803] | 219 | RT_VALID_PTR(pszFunction) ? pszFunction : "");
|
---|
[95809] | 220 | # ifdef IPRT_WITH_ASSERT_STACK
|
---|
[73762] | 221 | fprintf(stderr, "Stack :\n%s\n", szStack);
|
---|
[95809] | 222 | # endif
|
---|
[25518] | 223 | fflush(stderr);
|
---|
[95809] | 224 | # else
|
---|
| 225 | char szMsg[2048];
|
---|
| 226 | size_t cchMsg = RTStrPrintf(szMsg, sizeof(szMsg),
|
---|
| 227 | "\n!!Assertion Failed!!\n"
|
---|
| 228 | "Expression: %s\n"
|
---|
| 229 | "Location : %s(%d) %s\n",
|
---|
| 230 | RT_VALID_PTR(pszExpr) ? pszExpr : "<none>",
|
---|
| 231 | RT_VALID_PTR(pszFile) ? pszFile : "<none>",
|
---|
| 232 | uLine,
|
---|
| 233 | RT_VALID_PTR(pszFunction) ? pszFunction : "");
|
---|
| 234 | RTLogWriteStdErr(szMsg, cchMsg);
|
---|
| 235 | # ifdef IPRT_WITH_ASSERT_STACK
|
---|
| 236 | RTLogWriteStdErr(RT_STR_TUPLE("Stack :\n"));
|
---|
| 237 | RTLogWriteStdErr(szStack, strlen(szStack));
|
---|
| 238 | RTLogWriteStdErr(RT_STR_TUPLE("\n"));
|
---|
| 239 | # endif
|
---|
| 240 | # endif
|
---|
[25528] | 241 | # endif
|
---|
| 242 | #endif /* !IN_RING0 */
|
---|
[37233] | 243 |
|
---|
| 244 | RTErrVarsRestore(&SavedErrVars);
|
---|
[25518] | 245 | }
|
---|
[1] | 246 | }
|
---|
[25528] | 247 | RT_EXPORT_SYMBOL(RTAssertMsg1);
|
---|
[1] | 248 |
|
---|
| 249 |
|
---|
[25536] | 250 | /**
|
---|
| 251 | * Worker for RTAssertMsg2V and RTAssertMsg2AddV
|
---|
| 252 | *
|
---|
| 253 | * @param fInitial True if it's RTAssertMsg2V, otherwise false.
|
---|
| 254 | * @param pszFormat The message format string.
|
---|
| 255 | * @param va The format arguments.
|
---|
| 256 | */
|
---|
| 257 | static void rtAssertMsg2Worker(bool fInitial, const char *pszFormat, va_list va)
|
---|
[1] | 258 | {
|
---|
[25528] | 259 | va_list vaCopy;
|
---|
[25536] | 260 | size_t cch;
|
---|
[1] | 261 |
|
---|
[25528] | 262 | /*
|
---|
| 263 | * The global first.
|
---|
| 264 | */
|
---|
[25536] | 265 | if (fInitial)
|
---|
| 266 | {
|
---|
| 267 | va_copy(vaCopy, va);
|
---|
| 268 | cch = RTStrPrintfV(g_szRTAssertMsg2, sizeof(g_szRTAssertMsg2), pszFormat, vaCopy);
|
---|
| 269 | ASMAtomicWriteU32(&g_cchRTAssertMsg2, (uint32_t)cch);
|
---|
| 270 | va_end(vaCopy);
|
---|
| 271 | }
|
---|
| 272 | else
|
---|
| 273 | {
|
---|
| 274 | cch = ASMAtomicReadU32(&g_cchRTAssertMsg2);
|
---|
| 275 | if (cch < sizeof(g_szRTAssertMsg2) - 4)
|
---|
| 276 | {
|
---|
| 277 | va_copy(vaCopy, va);
|
---|
| 278 | cch += RTStrPrintfV(&g_szRTAssertMsg2[cch], sizeof(g_szRTAssertMsg2) - cch, pszFormat, vaCopy);
|
---|
| 279 | ASMAtomicWriteU32(&g_cchRTAssertMsg2, (uint32_t)cch);
|
---|
| 280 | va_end(vaCopy);
|
---|
| 281 | }
|
---|
| 282 | }
|
---|
[25528] | 283 |
|
---|
| 284 | /*
|
---|
| 285 | * If not quiet, make some noise.
|
---|
| 286 | */
|
---|
[25518] | 287 | if (!RTAssertAreQuiet())
|
---|
| 288 | {
|
---|
[37233] | 289 | RTERRVARS SavedErrVars;
|
---|
| 290 | RTErrVarsSave(&SavedErrVars);
|
---|
| 291 |
|
---|
[25528] | 292 | #ifdef IN_RING0
|
---|
| 293 | # ifdef IN_GUEST_R0
|
---|
| 294 | va_copy(vaCopy, va);
|
---|
| 295 | RTLogBackdoorPrintfV(pszFormat, vaCopy);
|
---|
| 296 | va_end(vaCopy);
|
---|
| 297 | # endif
|
---|
| 298 | /** @todo fully integrate this with the logger... play safe a bit for now. */
|
---|
[25536] | 299 | rtR0AssertNativeMsg2V(fInitial, pszFormat, va);
|
---|
[1] | 300 |
|
---|
[25528] | 301 | #else /* !IN_RING0 */
|
---|
[96448] | 302 |
|
---|
| 303 | # if defined(IN_RING3) && (defined(IN_RT_STATIC) || defined(IPRT_NO_CRT))
|
---|
| 304 | if (g_pfnRTLogAssert)
|
---|
[25528] | 305 | # endif
|
---|
[25518] | 306 | {
|
---|
[25528] | 307 | va_copy(vaCopy, va);
|
---|
[96448] | 308 | # if defined(IN_RING3) && (defined(IN_RT_STATIC) || defined(IPRT_NO_CRT))
|
---|
| 309 | g_pfnRTLogAssertV(pszFormat, vaCopy);
|
---|
| 310 | # else
|
---|
| 311 | RTLogAssertV(pszFormat, vaCopy);
|
---|
[25528] | 312 | # endif
|
---|
| 313 | va_end(vaCopy);
|
---|
[25518] | 314 | }
|
---|
[1] | 315 |
|
---|
[25528] | 316 | # ifdef IN_RING3
|
---|
[25518] | 317 | /* print to stderr, helps user and gdb debugging. */
|
---|
[36827] | 318 | char szMsg[sizeof(g_szRTAssertMsg2)];
|
---|
[25528] | 319 | va_copy(vaCopy, va);
|
---|
[95809] | 320 | size_t cchMsg = RTStrPrintfV(szMsg, sizeof(szMsg), pszFormat, vaCopy);
|
---|
[25528] | 321 | va_end(vaCopy);
|
---|
[95809] | 322 | # ifndef IPRT_NO_CRT
|
---|
| 323 | fwrite(szMsg, 1, cchMsg, stderr);
|
---|
[25518] | 324 | fflush(stderr);
|
---|
[95809] | 325 | # else
|
---|
| 326 | RTLogWriteStdErr(szMsg, cchMsg);
|
---|
| 327 | # endif
|
---|
[25528] | 328 | # endif
|
---|
| 329 | #endif /* !IN_RING0 */
|
---|
[37233] | 330 |
|
---|
| 331 | RTErrVarsRestore(&SavedErrVars);
|
---|
[25518] | 332 | }
|
---|
[1] | 333 | }
|
---|
[25536] | 334 |
|
---|
| 335 |
|
---|
| 336 | RTDECL(void) RTAssertMsg2V(const char *pszFormat, va_list va)
|
---|
| 337 | {
|
---|
| 338 | rtAssertMsg2Worker(true /*fInitial*/, pszFormat, va);
|
---|
| 339 | }
|
---|
[25528] | 340 | RT_EXPORT_SYMBOL(RTAssertMsg2V);
|
---|
[1] | 341 |
|
---|
[25536] | 342 |
|
---|
| 343 | RTDECL(void) RTAssertMsg2AddV(const char *pszFormat, va_list va)
|
---|
| 344 | {
|
---|
| 345 | rtAssertMsg2Worker(false /*fInitial*/, pszFormat, va);
|
---|
| 346 | }
|
---|
| 347 | RT_EXPORT_SYMBOL(RTAssertMsg2AddV);
|
---|
| 348 |
|
---|