- Timestamp:
- Sep 4, 2008 5:54:01 PM (16 years ago)
- svn:sync-xref-src-repo-rev:
- 36040
- Location:
- trunk/src/VBox
- Files:
-
- 4 edited
- 3 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/HostDrivers/Support/linux/Makefile
r11725 r12099 103 103 ifeq ($(BUILD_TYPE),debug) 104 104 OBJS += common/log/log.o \ 105 common/log/logellipsis.o \ 106 common/log/logrel.o \ 107 common/log/logrelellipsis.o \ 105 108 common/log/logcom.o \ 106 109 common/log/logformat.o \ -
trunk/src/VBox/HostDrivers/Support/linux/files_vboxdrv
r11752 r12099 68 68 ${PATH_ROOT}/src/VBox/Runtime/common/err/RTErrConvertFromErrno.cpp=>common/err/RTErrConvertFromErrno.c \ 69 69 ${PATH_ROOT}/src/VBox/Runtime/common/log/log.cpp=>common/log/log.c \ 70 ${PATH_ROOT}/src/VBox/Runtime/common/log/logellipsis.cpp=>common/log/logellipsis.c \ 71 ${PATH_ROOT}/src/VBox/Runtime/common/log/logrel.cpp=>common/log/logrel.c \ 72 ${PATH_ROOT}/src/VBox/Runtime/common/log/logrelellipsis.cpp=>common/log/logrelellipsis.c \ 70 73 ${PATH_ROOT}/src/VBox/Runtime/common/log/logcom.cpp=>common/log/logcom.c \ 71 74 ${PATH_ROOT}/src/VBox/Runtime/common/log/logformat.cpp=>common/log/logformat.c \ -
trunk/src/VBox/Runtime/Makefile.kmk
r11916 r12099 187 187 common/ldr/ldrPE.cpp \ 188 188 common/log/log.cpp \ 189 common/log/logellipsis.cpp \ 190 common/log/logrel.cpp \ 191 common/log/logrelellipsis.cpp \ 189 192 common/log/logcom.cpp \ 190 193 common/log/logformat.cpp \ … … 828 831 common/checksum/md5.cpp \ 829 832 common/checksum/ipv4.cpp \ 833 common/log/logellipsis.cpp \ 834 common/log/logrelellipsis.cpp \ 830 835 common/log/logcom.cpp \ 831 836 common/log/logformat.cpp \ … … 929 934 930 935 RuntimeR0Drv_SOURCES = \ 936 common/alloc/alloc.cpp \ 931 937 common/checksum/crc32.cpp \ 932 938 common/checksum/crc64.cpp \ … … 934 940 common/checksum/ipv4.cpp \ 935 941 common/log/log.cpp \ 942 common/log/logellipsis.cpp \ 943 common/log/logrel.cpp \ 944 common/log/logrelellipsis.cpp \ 936 945 common/log/logcom.cpp \ 937 946 common/log/logformat.cpp \ … … 1283 1292 RuntimeGC_SOURCES = \ 1284 1293 common/log/log.cpp \ 1294 common/log/logellipsis.cpp \ 1295 common/log/logrel.cpp \ 1296 common/log/logrelellipsis.cpp \ 1285 1297 common/log/logcom.cpp \ 1286 1298 common/log/logformat.cpp \ -
trunk/src/VBox/Runtime/common/log/log.cpp
r11853 r12099 95 95 /** Default logger instance. */ 96 96 extern "C" DECLIMPORT(RTLOGGERRC) g_Logger; 97 /** Default relese logger instance. */98 extern "C" DECLIMPORT(RTLOGGERRC) g_RelLogger;99 97 #else /* !IN_GC */ 100 98 /** Default logger instance. */ 101 99 static PRTLOGGER g_pLogger; 102 /** Default release logger instance. */103 static PRTLOGGER g_pRelLogger;104 100 #endif /* !IN_GC */ 105 101 #ifdef IN_RING3 … … 934 930 * we update *ppachMask on match. 935 931 */ 936 static bool rtlogIsGroupMatching(const char *pszGrp, const char **ppachMask, unsignedcchMask)932 static bool rtlogIsGroupMatching(const char *pszGrp, const char **ppachMask, size_t cchMask) 937 933 { 938 934 const char *pachMask; … … 1520 1516 1521 1517 /** 1522 * Gets the default release logger instance.1523 *1524 * @returns Pointer to default release logger instance.1525 * @returns NULL if no default release logger instance available.1526 */1527 RTDECL(PRTLOGGER) RTLogRelDefaultInstance(void)1528 {1529 #ifdef IN_GC1530 return &g_RelLogger;1531 #else /* !IN_GC */1532 return g_pRelLogger;1533 #endif /* !IN_GC */1534 }1535 1536 1537 #ifndef IN_GC1538 /**1539 * Sets the default logger instance.1540 *1541 * @returns iprt status code.1542 * @param pLogger The new default release logger instance.1543 */1544 RTDECL(PRTLOGGER) RTLogRelSetDefaultInstance(PRTLOGGER pLogger)1545 {1546 return (PRTLOGGER)ASMAtomicXchgPtr((void * volatile *)&g_pRelLogger, pLogger);1547 }1548 #endif /* !IN_GC */1549 1550 1551 /**1552 * Write to a logger instance.1553 *1554 * @param pLogger Pointer to logger instance.1555 * @param pvCallerRet Ignored.1556 * @param pszFormat Format string.1557 * @param ... Format arguments.1558 */1559 RTDECL(void) RTLogLogger(PRTLOGGER pLogger, void *pvCallerRet, const char *pszFormat, ...)1560 {1561 va_list args;1562 va_start(args, pszFormat);1563 #if defined(RT_OS_DARWIN) && defined(RT_ARCH_X86) && defined(IN_RING3)1564 /* manually align the stack before doing the call.1565 * We boldly assume that there is a stack frame here! */1566 __asm__ __volatile__("andl $-32, %%esp\t\n" ::: "%esp");1567 RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);1568 #else1569 RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);1570 #endif1571 va_end(args);1572 }1573 1574 1575 /**1576 1518 * Write to a logger instance. 1577 1519 * … … 1583 1525 { 1584 1526 RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args); 1585 }1586 1587 1588 /**1589 * Write to a logger instance.1590 *1591 * This function will check whether the instance, group and flags makes up a1592 * logging kind which is currently enabled before writing anything to the log.1593 *1594 * @param pLogger Pointer to logger instance. If NULL the default logger instance will be attempted.1595 * @param fFlags The logging flags.1596 * @param iGroup The group.1597 * The value ~0U is reserved for compatability with RTLogLogger[V] and is1598 * only for internal usage!1599 * @param pszFormat Format string.1600 * @param ... Format arguments.1601 * @remark This is a worker function of LogIt.1602 */1603 RTDECL(void) RTLogLoggerEx(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, ...)1604 {1605 va_list args;1606 va_start(args, pszFormat);1607 RTLogLoggerExV(pLogger, fFlags, iGroup, pszFormat, args);1608 va_end(args);1609 1527 } 1610 1528 … … 1626 1544 RTDECL(void) RTLogLoggerExV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args) 1627 1545 { 1546 int rc; 1547 1628 1548 /* 1629 1549 * A NULL logger means default instance. … … 1635 1555 return; 1636 1556 } 1637 rtlogLogger(pLogger, fFlags, iGroup, pszFormat, args);1638 }1639 1640 1641 /**1642 * Write to a logger instance, defaulting to the release one.1643 *1644 * This function will check whether the instance, group and flags makes up a1645 * logging kind which is currently enabled before writing anything to the log.1646 *1647 * @param pLogger Pointer to logger instance.1648 * @param fFlags The logging flags.1649 * @param iGroup The group.1650 * The value ~0U is reserved for compatability with RTLogLogger[V] and is1651 * only for internal usage!1652 * @param pszFormat Format string.1653 * @param ... Format arguments.1654 * @remark This is a worker function for LogRelIt.1655 */1656 RTDECL(void) RTLogRelLogger(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, ...)1657 {1658 va_list args;1659 va_start(args, pszFormat);1660 RTLogRelLoggerV(pLogger, fFlags, iGroup, pszFormat, args);1661 va_end(args);1662 }1663 1664 1665 /**1666 * Write to a logger instance, defaulting to the release one.1667 *1668 * This function will check whether the instance, group and flags makes up a1669 * logging kind which is currently enabled before writing anything to the log.1670 *1671 * @param pLogger Pointer to logger instance. If NULL the default release instance is attempted.1672 * @param fFlags The logging flags.1673 * @param iGroup The group.1674 * The value ~0U is reserved for compatability with RTLogLogger[V] and is1675 * only for internal usage!1676 * @param pszFormat Format string.1677 * @param args Format arguments.1678 */1679 RTDECL(void) RTLogRelLoggerV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)1680 {1681 /*1682 * A NULL logger means default instance.1683 */1684 if (!pLogger)1685 {1686 pLogger = RTLogRelDefaultInstance();1687 if (!pLogger)1688 return;1689 }1690 rtlogLogger(pLogger, fFlags, iGroup, pszFormat, args);1691 }1692 1693 1694 /**1695 * Worker for the RTLog[Rel]Logger*() functions.1696 *1697 * @param pLogger Pointer to logger instance.1698 * @param fFlags The logging flags.1699 * @param iGroup The group.1700 * The value ~0U is reserved for compatability with RTLogLogger[V] and is1701 * only for internal usage!1702 * @param pszFormat Format string.1703 * @param args Format arguments.1704 */1705 static void rtlogLogger(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)1706 {1707 int rc;1708 1557 1709 1558 /* … … 1758 1607 1759 1608 /** 1760 * printf like function for writing to the default log.1761 *1762 * @param pszFormat Printf like format string.1763 * @param ... Optional arguments as specified in pszFormat.1764 *1765 * @remark The API doesn't support formatting of floating point numbers at the moment.1766 */1767 RTDECL(void) RTLogPrintf(const char *pszFormat, ...)1768 {1769 va_list args;1770 va_start(args, pszFormat);1771 RTLogPrintfV(pszFormat, args);1772 va_end(args);1773 }1774 1775 1776 /**1777 1609 * vprintf like function for writing to the default log. 1778 1610 * … … 1785 1617 { 1786 1618 RTLogLoggerV(NULL, pszFormat, args); 1787 }1788 1789 1790 /**1791 * printf like function for writing to the default release log.1792 *1793 * @param pszFormat Printf like format string.1794 * @param ... Optional arguments as specified in pszFormat.1795 *1796 * @remark The API doesn't support formatting of floating point numbers at the moment.1797 */1798 RTDECL(void) RTLogRelPrintf(const char *pszFormat, ...)1799 {1800 va_list args;1801 va_start(args, pszFormat);1802 RTLogRelPrintfV(pszFormat, args);1803 va_end(args);1804 }1805 1806 1807 /**1808 * vprintf like function for writing to the default release log.1809 *1810 * @param pszFormat Printf like format string.1811 * @param args Optional arguments as specified in pszFormat.1812 *1813 * @remark The API doesn't support formatting of floating point numbers at the moment.1814 */1815 RTDECL(void) RTLogRelPrintfV(const char *pszFormat, va_list args)1816 {1817 RTLogRelLoggerV(NULL, 0, ~0U, pszFormat, args);1818 1619 } 1819 1620 -
trunk/src/VBox/Runtime/common/log/logellipsis.cpp
r12036 r12099 1 1 /* $Id$ */ 2 2 /** @file 3 * Runtime VBox - Logger .3 * Runtime VBox - Logger, the ellipsis variants. 4 4 */ 5 5 … … 29 29 */ 30 30 31 32 31 /******************************************************************************* 33 32 * Header Files * 34 33 *******************************************************************************/ 35 34 #include <iprt/log.h> 36 #ifndef IN_GC37 # include <iprt/alloc.h>38 # include <iprt/process.h>39 # include <iprt/semaphore.h>40 # include <iprt/thread.h>41 # include <iprt/mp.h>42 #endif43 #ifdef IN_RING344 # include <iprt/file.h>45 # include <iprt/path.h>46 #endif47 #include <iprt/time.h>48 35 #include <iprt/asm.h> 49 #include <iprt/assert.h>50 #include <iprt/err.h>51 #include <iprt/param.h>52 53 36 #include <iprt/stdarg.h> 54 #include <iprt/string.h>55 #include <iprt/ctype.h>56 #ifdef IN_RING357 # include <iprt/alloca.h>58 # include <stdio.h>59 #endif60 61 62 /*******************************************************************************63 * Structures and Typedefs *64 *******************************************************************************/65 /**66 * Arguments passed to the output function.67 */68 typedef struct RTLOGOUTPUTPREFIXEDARGS69 {70 /** The logger instance. */71 PRTLOGGER pLogger;72 /** The flags. (used for prefixing.) */73 unsigned fFlags;74 /** The group. (used for prefixing.) */75 unsigned iGroup;76 } RTLOGOUTPUTPREFIXEDARGS, *PRTLOGOUTPUTPREFIXEDARGS;77 78 79 /*******************************************************************************80 * Internal Functions *81 *******************************************************************************/82 #ifndef IN_GC83 static unsigned rtlogGroupFlags(const char *psz);84 #endif85 static void rtlogLogger(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args);86 static void rtlogFlush(PRTLOGGER pLogger);87 static DECLCALLBACK(size_t) rtLogOutput(void *pv, const char *pachChars, size_t cbChars);88 static DECLCALLBACK(size_t) rtLogOutputPrefixed(void *pv, const char *pachChars, size_t cbChars);89 90 91 /*******************************************************************************92 * Global Variables *93 *******************************************************************************/94 #ifdef IN_GC95 /** Default logger instance. */96 extern "C" DECLIMPORT(RTLOGGERRC) g_Logger;97 /** Default relese logger instance. */98 extern "C" DECLIMPORT(RTLOGGERRC) g_RelLogger;99 #else /* !IN_GC */100 /** Default logger instance. */101 static PRTLOGGER g_pLogger;102 /** Default release logger instance. */103 static PRTLOGGER g_pRelLogger;104 #endif /* !IN_GC */105 #ifdef IN_RING3106 /** The RTThreadGetWriteLockCount() change caused by the logger mutex semaphore. */107 static uint32_t volatile g_cLoggerLockCount;108 #endif109 #ifdef IN_RING0110 /** Number of per-thread loggers. */111 static int32_t volatile g_cPerThreadLoggers;112 /** Per-thread loggers.113 * This is just a quick TLS hack suitable for debug logging only.114 * If we run out of entries, just unload and reload the driver. */115 static struct RTLOGGERPERTHREAD116 {117 /** The thread. */118 RTNATIVETHREAD volatile NativeThread;119 /** The (process / session) key. */120 uintptr_t volatile uKey;121 /** The logger instance.*/122 PRTLOGGER volatile pLogger;123 } g_aPerThreadLoggers[8] =124 { { NIL_RTNATIVETHREAD, 0, 0},125 { NIL_RTNATIVETHREAD, 0, 0},126 { NIL_RTNATIVETHREAD, 0, 0},127 { NIL_RTNATIVETHREAD, 0, 0},128 { NIL_RTNATIVETHREAD, 0, 0},129 { NIL_RTNATIVETHREAD, 0, 0},130 { NIL_RTNATIVETHREAD, 0, 0},131 { NIL_RTNATIVETHREAD, 0, 0}132 };133 #endif /* IN_RING0 */134 135 136 /**137 * Locks the logger instance.138 *139 * @returns See RTSemFastMutexRequest().140 * @param pLogger The logger instance.141 */142 DECLINLINE(int) rtlogLock(PRTLOGGER pLogger)143 {144 #ifndef IN_GC145 if (pLogger->MutexSem != NIL_RTSEMFASTMUTEX)146 {147 int rc = RTSemFastMutexRequest(pLogger->MutexSem);148 AssertRCReturn(rc, rc);149 }150 #endif151 return VINF_SUCCESS;152 }153 154 155 /**156 * Unlocks the logger instance.157 * @param pLogger The logger instance.158 */159 DECLINLINE(void) rtlogUnlock(PRTLOGGER pLogger)160 {161 #ifndef IN_GC162 if (pLogger->MutexSem != NIL_RTSEMFASTMUTEX)163 RTSemFastMutexRelease(pLogger->MutexSem);164 #endif165 return;166 }167 168 169 #ifndef IN_GC170 /**171 * Create a logger instance, comprehensive version.172 *173 * @returns iprt status code.174 *175 * @param ppLogger Where to store the logger instance.176 * @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.177 * @param pszGroupSettings The initial group settings.178 * @param pszEnvVarBase Base name for the environment variables for this instance.179 * @param cGroups Number of groups in the array.180 * @param papszGroups Pointer to array of groups. This must stick around for the life of the181 * logger instance.182 * @param fDestFlags The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.183 * @param pszErrorMsg A buffer which is filled with an error message if something fails. May be NULL.184 * @param cchErrorMsg The size of the error message buffer.185 * @param pszFilenameFmt Log filename format string. Standard RTStrFormat().186 * @param ... Format arguments.187 */188 RTDECL(int) RTLogCreateExV(PRTLOGGER *ppLogger, RTUINT fFlags, const char *pszGroupSettings,189 const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,190 RTUINT fDestFlags, char *pszErrorMsg, size_t cchErrorMsg, const char *pszFilenameFmt, va_list args)191 {192 int rc;193 size_t cb;194 PRTLOGGER pLogger;195 196 /*197 * Validate input.198 */199 if ( (cGroups && !papszGroups)200 || !VALID_PTR(ppLogger)201 )202 {203 AssertMsgFailed(("Invalid parameters!\n"));204 return VERR_INVALID_PARAMETER;205 }206 *ppLogger = NULL;207 208 if (pszErrorMsg)209 RTStrPrintf(pszErrorMsg, cchErrorMsg, "unknown error");210 211 /*212 * Allocate a logger instance.213 */214 cb = RT_OFFSETOF(RTLOGGER, afGroups[cGroups + 1]) + RTPATH_MAX;215 pLogger = (PRTLOGGER)RTMemAllocZ(cb);216 if (pLogger)217 {218 uint8_t *pu8Code;219 220 pLogger->u32Magic = RTLOGGER_MAGIC;221 pLogger->papszGroups = papszGroups;222 pLogger->cMaxGroups = cGroups;223 pLogger->cGroups = cGroups;224 pLogger->pszFilename = (char *)&pLogger->afGroups[cGroups + 1];225 pLogger->File = NIL_RTFILE;226 pLogger->fFlags = fFlags;227 pLogger->fDestFlags = fDestFlags;228 pLogger->fPendingPrefix = true;229 if (pszGroupSettings)230 RTLogGroupSettings(pLogger, pszGroupSettings);231 232 /*233 * Emit wrapper code.234 */235 pu8Code = (uint8_t *)RTMemExecAlloc(64);236 if (pu8Code)237 {238 pLogger->pfnLogger = *(PFNRTLOGGER*)&pu8Code;239 #ifdef RT_ARCH_AMD64240 /* this wrapper will not be used on AMD64, we will be requiring C99 compilers there. */241 *pu8Code++ = 0xcc;242 #else243 *pu8Code++ = 0x68; /* push imm32 */244 *(void **)pu8Code = pLogger;245 pu8Code += sizeof(void *);246 *pu8Code++ = 0xe8; /* call rel32 */247 *(uint32_t *)pu8Code = (uintptr_t)RTLogLogger - ((uintptr_t)pu8Code + sizeof(uint32_t));248 pu8Code += sizeof(uint32_t);249 *pu8Code++ = 0x8d; /* lea esp, [esp + 4] */250 *pu8Code++ = 0x64;251 *pu8Code++ = 0x24;252 *pu8Code++ = 0x04;253 *pu8Code++ = 0xc3; /* ret near */254 #endif255 AssertMsg((uintptr_t)pu8Code - (uintptr_t)pLogger->pfnLogger <= 64,256 ("Wrapper assembly is too big! %d bytes\n", (uintptr_t)pu8Code - (uintptr_t)pLogger->pfnLogger));257 258 259 #ifdef IN_RING3 /* files and env.vars. are only accessible when in R3 at the present time. */260 /*261 * Format the filename.262 */263 if (pszFilenameFmt)264 {265 RTStrPrintfV(pLogger->pszFilename, RTPATH_MAX, pszFilenameFmt, args);266 pLogger->fDestFlags |= RTLOGDEST_FILE;267 }268 269 /*270 * Parse the environment variables.271 */272 if (pszEnvVarBase)273 {274 /* make temp copy of environment variable base. */275 size_t cchEnvVarBase = strlen(pszEnvVarBase);276 char *pszEnvVar = (char *)alloca(cchEnvVarBase + 16);277 memcpy(pszEnvVar, pszEnvVarBase, cchEnvVarBase);278 279 /*280 * Destination.281 */282 strcpy(pszEnvVar + cchEnvVarBase, "_DEST");283 const char *pszVar = getenv(pszEnvVar);284 if (pszVar)285 {286 while (*pszVar)287 {288 /* skip blanks. */289 while (RT_C_IS_SPACE(*pszVar))290 pszVar++;291 if (!*pszVar)292 break;293 294 /* parse instruction. */295 static struct296 {297 const char *pszInstr;298 unsigned fFlag;299 } const aDest[] =300 {301 { "file", RTLOGDEST_FILE }, /* Must be 1st! */302 { "dir", RTLOGDEST_FILE }, /* Must be 2nd! */303 { "stdout", RTLOGDEST_STDOUT },304 { "stderr", RTLOGDEST_STDERR },305 { "debugger", RTLOGDEST_DEBUGGER },306 { "com", RTLOGDEST_COM },307 { "user", RTLOGDEST_USER },308 };309 310 /* check no prefix. */311 bool fNo = false;312 if (pszVar[0] == 'n' && pszVar[1] == 'o')313 {314 fNo = true;315 pszVar += 2;316 }317 318 /* instruction. */319 unsigned i;320 for (i = 0; i < ELEMENTS(aDest); i++)321 {322 size_t cchInstr = strlen(aDest[i].pszInstr);323 if (!strncmp(pszVar, aDest[i].pszInstr, cchInstr))324 {325 if (!fNo)326 pLogger->fDestFlags |= aDest[i].fFlag;327 else328 pLogger->fDestFlags &= ~aDest[i].fFlag;329 pszVar += cchInstr;330 331 /* check for value. */332 while (RT_C_IS_SPACE(*pszVar))333 pszVar++;334 if (*pszVar == '=' || *pszVar == ':')335 {336 pszVar++;337 const char *pszEnd = strchr(pszVar, ';');338 if (!pszEnd)339 pszEnd = strchr(pszVar, '\0');340 341 /* log file name */342 size_t cch = pszEnd - pszVar;343 if (i == 0 /* file */ && !fNo)344 {345 memcpy(pLogger->pszFilename, pszVar, cch);346 pLogger->pszFilename[cch] = '\0';347 }348 /* log directory */349 else if (i == 1 /* dir */ && !fNo)350 {351 char szTmp[RTPATH_MAX];352 const char *pszFile = RTPathFilename(pLogger->pszFilename);353 if (pszFile)354 strcpy(szTmp, pszFile);355 else356 pszFile = ""; /* you've screwed up, sir. */357 358 memcpy(pLogger->pszFilename, pszVar, cch);359 pLogger->pszFilename[cch] = '\0';360 RTPathStripTrailingSlash(pLogger->pszFilename);361 362 cch = strlen(pLogger->pszFilename);363 pLogger->pszFilename[cch++] = '/';364 strcpy(&pLogger->pszFilename[cch], szTmp);365 }366 else367 AssertMsgFailed(("Invalid %s_DEST! %s%s doesn't take a value!\n", pszEnvVarBase, fNo ? "no" : "", aDest[i].pszInstr));368 pszVar = pszEnd + (*pszEnd != '\0');369 }370 break;371 }372 }373 /* unknown instruction? */374 if (i >= ELEMENTS(aDest))375 {376 AssertMsgFailed(("Invalid %s_DEST! unknown instruction %.20s\n", pszEnvVarBase, pszVar));377 pszVar++;378 }379 380 /* skip blanks and delimiters. */381 while (RT_C_IS_SPACE(*pszVar) || *pszVar == ';')382 pszVar++;383 } /* while more environment variable value left */384 }385 386 /*387 * The flags.388 */389 strcpy(pszEnvVar + cchEnvVarBase, "_FLAGS");390 pszVar = getenv(pszEnvVar);391 if (pszVar)392 RTLogFlags(pLogger, pszVar);393 394 /*395 * The group settings.396 */397 pszEnvVar[cchEnvVarBase] = '\0';398 pszVar = getenv(pszEnvVar);399 if (pszVar)400 RTLogGroupSettings(pLogger, pszVar);401 }402 #endif /* IN_RING3 */403 404 /*405 * Open the destination(s).406 */407 rc = VINF_SUCCESS;408 #ifdef IN_RING3409 if (pLogger->fDestFlags & RTLOGDEST_FILE)410 {411 if (!(pLogger->fFlags & RTLOGFLAGS_APPEND))412 rc = RTFileOpen(&pLogger->File, pLogger->pszFilename,413 RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE);414 else415 {416 /** @todo RTFILE_O_APPEND. */417 rc = RTFileOpen(&pLogger->File, pLogger->pszFilename,418 RTFILE_O_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE);419 if (RT_SUCCESS(rc))420 {421 rc = RTFileSeek(pLogger->File, 0, RTFILE_SEEK_END, NULL);422 if (RT_FAILURE(rc))423 {424 RTFileClose(pLogger->File);425 pLogger->File = NIL_RTFILE;426 }427 }428 }429 if (RT_FAILURE(rc) && pszErrorMsg)430 RTStrPrintf(pszErrorMsg, cchErrorMsg, "could not open file '%s'", pLogger->pszFilename);431 }432 #endif /* IN_RING3 */433 434 /*435 * Create mutex and check how much it counts when entering the lock436 * so that we can report the values for RTLOGFLAGS_PREFIX_LOCK_COUNTS.437 */438 if (RT_SUCCESS(rc))439 {440 rc = RTSemFastMutexCreate(&pLogger->MutexSem);441 if (RT_SUCCESS(rc))442 {443 #ifdef IN_RING3 /** @todo do counters in ring-0 too? */444 RTTHREAD Thread = RTThreadSelf();445 if (Thread != NIL_RTTHREAD)446 {447 int32_t c = RTThreadGetWriteLockCount(Thread);448 RTSemFastMutexRequest(pLogger->MutexSem);449 c = RTThreadGetWriteLockCount(Thread) - c;450 RTSemFastMutexRelease(pLogger->MutexSem);451 ASMAtomicWriteU32(&g_cLoggerLockCount, c);452 }453 #endif454 *ppLogger = pLogger;455 return VINF_SUCCESS;456 }457 458 if (pszErrorMsg)459 RTStrPrintf(pszErrorMsg, cchErrorMsg, "failed to create sempahore");460 }461 #ifdef IN_RING3462 RTFileClose(pLogger->File);463 #endif464 RTMemExecFree(*(void **)&pLogger->pfnLogger);465 }466 else467 {468 #ifdef RT_OS_LINUX469 /*470 * RTMemAlloc() succeeded but RTMemExecAlloc() failed -- most probably an SELinux problem.471 */472 if (pszErrorMsg)473 RTStrPrintf(pszErrorMsg, cchErrorMsg, "mmap(PROT_WRITE | PROT_EXEC) failed -- SELinux?");474 #endif /* RT_OS_LINUX */475 rc = VERR_NO_MEMORY;476 }477 RTMemFree(pLogger);478 }479 else480 rc = VERR_NO_MEMORY;481 482 return rc;483 }484 485 /**486 * Create a logger instance.487 *488 * @returns iprt status code.489 *490 * @param ppLogger Where to store the logger instance.491 * @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.492 * @param pszGroupSettings The initial group settings.493 * @param pszEnvVarBase Base name for the environment variables for this instance.494 * @param cGroups Number of groups in the array.495 * @param papszGroups Pointer to array of groups. This must stick around for the life of the496 * logger instance.497 * @param fDestFlags The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.498 * @param pszFilenameFmt Log filename format string. Standard RTStrFormat().499 * @param ... Format arguments.500 */501 RTDECL(int) RTLogCreate(PRTLOGGER *ppLogger, RTUINT fFlags, const char *pszGroupSettings,502 const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,503 RTUINT fDestFlags, const char *pszFilenameFmt, ...)504 {505 va_list args;506 int rc;507 508 va_start(args, pszFilenameFmt);509 rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups, fDestFlags, NULL, 0, pszFilenameFmt, args);510 va_end(args);511 return rc;512 }513 514 /**515 * Create a logger instance.516 *517 * @returns iprt status code.518 *519 * @param ppLogger Where to store the logger instance.520 * @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.521 * @param pszGroupSettings The initial group settings.522 * @param pszEnvVarBase Base name for the environment variables for this instance.523 * @param cGroups Number of groups in the array.524 * @param papszGroups Pointer to array of groups. This must stick around for the life of the525 * logger instance.526 * @param fDestFlags The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.527 * @param pszErrorMsg A buffer which is filled with an error message if something fails. May be NULL.528 * @param cchErrorMsg The size of the error message buffer.529 * @param pszFilenameFmt Log filename format string. Standard RTStrFormat().530 * @param ... Format arguments.531 */532 RTDECL(int) RTLogCreateEx(PRTLOGGER *ppLogger, RTUINT fFlags, const char *pszGroupSettings,533 const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,534 RTUINT fDestFlags, char *pszErrorMsg, size_t cchErrorMsg, const char *pszFilenameFmt, ...)535 {536 va_list args;537 int rc;538 539 va_start(args, pszFilenameFmt);540 rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups, fDestFlags, pszErrorMsg, cchErrorMsg, pszFilenameFmt, args);541 va_end(args);542 return rc;543 }544 545 /**546 * Destroys a logger instance.547 *548 * The instance is flushed and all output destinations closed (where applicable).549 *550 * @returns iprt status code.551 * @param pLogger The logger instance which close destroyed. NULL is fine.552 */553 RTDECL(int) RTLogDestroy(PRTLOGGER pLogger)554 {555 int rc;556 RTUINT iGroup;557 RTSEMFASTMUTEX MutexSem;558 559 /*560 * Validate input.561 */562 if (!pLogger)563 return VINF_SUCCESS;564 AssertReturn(VALID_PTR(pLogger), VERR_INVALID_POINTER);565 AssertReturn(pLogger->u32Magic == RTLOGGER_MAGIC, VERR_INVALID_MAGIC);566 567 /*568 * Acquire logger instance sem and disable all logging. (paranoia)569 */570 rc = rtlogLock(pLogger);571 if (RT_FAILURE(rc))572 return rc;573 574 pLogger->fFlags |= RTLOGFLAGS_DISABLED;575 iGroup = pLogger->cGroups;576 while (iGroup-- > 0)577 pLogger->afGroups[iGroup] = 0;578 579 /*580 * Flush it.581 */582 RTLogFlush(pLogger);583 584 /*585 * Close output stuffs.586 */587 #ifdef IN_RING3588 if (pLogger->File != NIL_RTFILE)589 {590 int rc2 = RTFileClose(pLogger->File);591 AssertRC(rc2);592 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))593 rc = rc2;594 pLogger->File = NIL_RTFILE;595 }596 #endif597 598 /*599 * Free the mutex and the instance memory.600 */601 MutexSem = pLogger->MutexSem;602 pLogger->MutexSem = NIL_RTSEMFASTMUTEX;603 if (MutexSem != NIL_RTSEMFASTMUTEX)604 {605 int rc2;606 RTSemFastMutexRelease(MutexSem);607 rc2 = RTSemFastMutexDestroy(MutexSem);608 AssertRC(rc2);609 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))610 rc = rc2;611 }612 613 RTMemFree(pLogger);614 615 return rc;616 }617 618 619 /**620 * Create a logger instance clone for RC usage.621 *622 * @returns iprt status code.623 *624 * @param pLogger The logger instance to be cloned.625 * @param pLoggerGC Where to create the GC logger instance.626 * @param cbLoggerGC Amount of memory allocated to for the GC logger instance clone.627 * @param pfnLoggerGCPtr Pointer to logger wrapper function for this instance (GC Ptr).628 * @param pfnFlushGCPtr Pointer to flush function (GC Ptr).629 * @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.630 */631 RTDECL(int) RTLogCloneRC(PRTLOGGER pLogger, PRTLOGGERRC pLoggerGC, size_t cbLoggerGC,632 RTRCPTR pfnLoggerGCPtr, RTRCPTR pfnFlushGCPtr, RTUINT fFlags)633 {634 /*635 * Validate input.636 */637 if ( !pLoggerGC638 || !pfnFlushGCPtr639 || !pfnLoggerGCPtr)640 {641 AssertMsgFailed(("Invalid parameters!\n"));642 return VERR_INVALID_PARAMETER;643 }644 if (cbLoggerGC < sizeof(*pLoggerGC))645 {646 AssertMsgFailed(("%d min=%d\n", cbLoggerGC, sizeof(*pLoggerGC)));647 return VERR_INVALID_PARAMETER;648 }649 650 /*651 * Initialize GC instance.652 */653 pLoggerGC->offScratch = 0;654 pLoggerGC->fPendingPrefix = false;655 pLoggerGC->pfnLogger = pfnLoggerGCPtr;656 pLoggerGC->pfnFlush = pfnFlushGCPtr;657 pLoggerGC->u32Magic = RTLOGGERRC_MAGIC;658 pLoggerGC->fFlags = fFlags | RTLOGFLAGS_DISABLED;659 pLoggerGC->cGroups = 1;660 pLoggerGC->afGroups[0] = 0;661 662 /*663 * Resolve defaults.664 */665 if (!pLogger)666 {667 pLogger = RTLogDefaultInstance();668 if (!pLogger)669 return VINF_SUCCESS;670 }671 672 /*673 * Check if there's enough space for the groups.674 */675 if (cbLoggerGC < (size_t)RT_OFFSETOF(RTLOGGERRC, afGroups[pLogger->cGroups]))676 {677 AssertMsgFailed(("%d req=%d cGroups=%d\n", cbLoggerGC, RT_OFFSETOF(RTLOGGERRC, afGroups[pLogger->cGroups]), pLogger->cGroups));678 return VERR_INVALID_PARAMETER;679 }680 memcpy(&pLoggerGC->afGroups[0], &pLogger->afGroups[0], pLogger->cGroups * sizeof(pLoggerGC->afGroups[0]));681 pLoggerGC->cGroups = pLogger->cGroups;682 683 /*684 * Copy bits from the HC instance.685 */686 pLoggerGC->fPendingPrefix = pLogger->fPendingPrefix;687 pLoggerGC->fFlags |= pLogger->fFlags;688 689 /*690 * Check if we can remove the disabled flag.691 */692 if ( pLogger->fDestFlags693 && !((pLogger->fFlags | fFlags) & RTLOGFLAGS_DISABLED))694 pLoggerGC->fFlags &= ~RTLOGFLAGS_DISABLED;695 696 return VINF_SUCCESS;697 }698 699 700 /**701 * Flushes a GC logger instance to a HC logger.702 *703 *704 * @returns iprt status code.705 * @param pLogger The HC logger instance to flush pLoggerGC to.706 * If NULL the default logger is used.707 * @param pLoggerGC The GC logger instance to flush.708 */709 RTDECL(void) RTLogFlushGC(PRTLOGGER pLogger, PRTLOGGERRC pLoggerGC)710 {711 /*712 * Resolve defaults.713 */714 if (!pLogger)715 {716 pLogger = RTLogDefaultInstance();717 if (!pLogger)718 {719 pLoggerGC->offScratch = 0;720 return;721 }722 }723 724 /*725 * Any thing to flush?726 */727 if ( pLogger->offScratch728 || pLoggerGC->offScratch)729 {730 /*731 * Acquire logger instance sem.732 */733 int rc = rtlogLock(pLogger);734 if (RT_FAILURE(rc))735 return;736 737 /*738 * Write whatever the GC instance contains to the HC one, and then739 * flush the HC instance.740 */741 if (pLoggerGC->offScratch)742 {743 rtLogOutput(pLogger, pLoggerGC->achScratch, pLoggerGC->offScratch);744 rtLogOutput(pLogger, NULL, 0);745 pLoggerGC->offScratch = 0;746 }747 748 /*749 * Release the semaphore.750 */751 rtlogUnlock(pLogger);752 }753 }754 755 756 #ifdef IN_RING3757 /**758 * Create a logger instance for singled threaded ring-0 usage.759 *760 * @returns iprt status code.761 *762 * @param pLogger Where to create the logger instance.763 * @param cbLogger The amount of memory available for the logger instance.764 * @param pfnLogger Pointer to logger wrapper function for the clone.765 * @param pfnFlush Pointer to flush function for the clone.766 * @param fFlags Logger instance flags for the clone, a combination of the RTLOGFLAGS_* values.767 * @param fDestFlags The destination flags.768 */769 RTDECL(int) RTLogCreateForR0(PRTLOGGER pLogger, size_t cbLogger, PFNRTLOGGER pfnLogger, PFNRTLOGFLUSH pfnFlush, RTUINT fFlags, RTUINT fDestFlags)770 {771 /*772 * Validate input.773 */774 AssertPtrReturn(pLogger, VERR_INVALID_PARAMETER);775 AssertReturn(cbLogger >= sizeof(*pLogger), VERR_INVALID_PARAMETER);776 AssertReturn(pfnLogger, VERR_INVALID_PARAMETER);777 AssertReturn(pfnFlush, VERR_INVALID_PARAMETER);778 779 /*780 * Initialize the ring-0 instance.781 */782 pLogger->offScratch = 0;783 pLogger->fPendingPrefix = false;784 pLogger->pfnLogger = pfnLogger;785 pLogger->pfnFlush = pfnFlush;786 pLogger->MutexSem = NIL_RTSEMFASTMUTEX; /* Not serialized. */787 pLogger->u32Magic = RTLOGGER_MAGIC;788 pLogger->fFlags = fFlags;789 pLogger->fDestFlags = fDestFlags & ~RTLOGDEST_FILE;790 pLogger->File = NIL_RTFILE;791 pLogger->pszFilename = NULL;792 pLogger->papszGroups = NULL;793 pLogger->cMaxGroups = (cbLogger - RT_OFFSETOF(RTLOGGER, afGroups[0])) / sizeof(pLogger->afGroups[0]);794 pLogger->cGroups = 1;795 pLogger->afGroups[0] = 0;796 return VINF_SUCCESS;797 }798 #endif /* IN_RING3 */799 800 801 /**802 * Copies the group settings and flags from logger instance to another.803 *804 * @returns IPRT status code.805 * @param pDstLogger The destination logger instance.806 * @param pSrcLogger The source logger instance. If NULL the default one is used.807 * @param fFlagsOr OR mask for the flags.808 * @param fFlagsAnd AND mask for the flags.809 */810 RTDECL(int) RTLogCopyGroupsAndFlags(PRTLOGGER pDstLogger, PCRTLOGGER pSrcLogger, unsigned fFlagsOr, unsigned fFlagsAnd)811 {812 int rc;813 unsigned cGroups;814 815 /*816 * Validate input.817 */818 AssertPtrReturn(pDstLogger, VERR_INVALID_PARAMETER);819 AssertPtrNullReturn(pSrcLogger, VERR_INVALID_PARAMETER);820 821 /*822 * Resolve defaults.823 */824 if (!pSrcLogger)825 {826 pSrcLogger = RTLogDefaultInstance();827 if (!pSrcLogger)828 {829 pDstLogger->fFlags |= RTLOGFLAGS_DISABLED;830 pDstLogger->cGroups = 1;831 pDstLogger->afGroups[0] = 0;832 return VINF_SUCCESS;833 }834 }835 836 /*837 * Copy flags and group settings.838 */839 pDstLogger->fFlags = (pSrcLogger->fFlags & fFlagsAnd) | fFlagsOr;840 841 rc = VINF_SUCCESS;842 cGroups = pSrcLogger->cGroups;843 if (cGroups < pDstLogger->cMaxGroups)844 {845 AssertMsgFailed(("cMaxGroups=%zd cGroups=%zd (min size %d)\n", pDstLogger->cMaxGroups,846 pSrcLogger->cGroups, RT_OFFSETOF(RTLOGGER, afGroups[pSrcLogger->cGroups])));847 rc = VERR_INVALID_PARAMETER;848 cGroups = pDstLogger->cMaxGroups;849 }850 memcpy(&pDstLogger->afGroups[0], &pSrcLogger->afGroups[0], cGroups * sizeof(pDstLogger->afGroups[0]));851 pDstLogger->cGroups = cGroups;852 853 return rc;854 }855 856 857 /**858 * Flushes the buffer in one logger instance onto another logger.859 *860 * @returns iprt status code.861 *862 * @param pSrcLogger The logger instance to flush.863 * @param pDstLogger The logger instance to flush onto.864 * If NULL the default logger will be used.865 */866 RTDECL(void) RTLogFlushToLogger(PRTLOGGER pSrcLogger, PRTLOGGER pDstLogger)867 {868 /*869 * Resolve defaults.870 */871 if (!pDstLogger)872 {873 pDstLogger = RTLogDefaultInstance();874 if (!pDstLogger)875 {876 /* flushing to "/dev/null". */877 if (pSrcLogger->offScratch)878 {879 int rc = rtlogLock(pSrcLogger);880 if (RT_SUCCESS(rc))881 {882 pSrcLogger->offScratch = 0;883 rtlogLock(pSrcLogger);884 }885 }886 return;887 }888 }889 890 /*891 * Any thing to flush?892 */893 if ( pSrcLogger->offScratch894 || pDstLogger->offScratch)895 {896 /*897 * Acquire logger semaphores.898 */899 int rc = rtlogLock(pDstLogger);900 if (RT_FAILURE(rc))901 return;902 rc = rtlogLock(pSrcLogger);903 if (RT_SUCCESS(rc))904 {905 /*906 * Write whatever the GC instance contains to the HC one, and then907 * flush the HC instance.908 */909 if (pSrcLogger->offScratch)910 {911 rtLogOutput(pDstLogger, pSrcLogger->achScratch, pSrcLogger->offScratch);912 rtLogOutput(pDstLogger, NULL, 0);913 pSrcLogger->offScratch = 0;914 }915 916 /*917 * Release the semaphores.918 */919 rtlogUnlock(pSrcLogger);920 }921 rtlogUnlock(pDstLogger);922 }923 }924 925 926 /**927 * Matches a group name with a pattern mask in an case insensitive manner (ASCII).928 *929 * @returns true if matching and *ppachMask set to the end of the pattern.930 * @returns false if no match.931 * @param pszGrp The group name.932 * @param ppachMask Pointer to the pointer to the mask. Only wildcard supported is '*'.933 * @param cchMask The length of the mask, including modifiers. The modifiers is why934 * we update *ppachMask on match.935 */936 static bool rtlogIsGroupMatching(const char *pszGrp, const char **ppachMask, unsigned cchMask)937 {938 const char *pachMask;939 940 if (!pszGrp || !*pszGrp)941 return false;942 pachMask = *ppachMask;943 for (;;)944 {945 if (RT_C_TO_LOWER(*pszGrp) != RT_C_TO_LOWER(*pachMask))946 {947 const char *pszTmp;948 949 /*950 * Check for wildcard and do a minimal match if found.951 */952 if (*pachMask != '*')953 return false;954 955 /* eat '*'s. */956 do pachMask++;957 while (--cchMask && *pachMask == '*');958 959 /* is there more to match? */960 if ( !cchMask961 || *pachMask == '.'962 || *pachMask == '=')963 break; /* we're good */964 965 /* do extremely minimal matching (fixme) */966 pszTmp = strchr(pszGrp, RT_C_TO_LOWER(*pachMask));967 if (!pszTmp)968 pszTmp = strchr(pszGrp, RT_C_TO_UPPER(*pachMask));969 if (!pszTmp)970 return false;971 pszGrp = pszTmp;972 continue;973 }974 975 /* done? */976 if (!*++pszGrp)977 {978 /* trailing wildcard is ok. */979 do980 {981 pachMask++;982 cchMask--;983 } while (cchMask && *pachMask == '*');984 if ( !cchMask985 || *pachMask == '.'986 || *pachMask == '=')987 break; /* we're good */988 return false;989 }990 991 if (!--cchMask)992 return false;993 pachMask++;994 }995 996 /* match */997 *ppachMask = pachMask;998 return true;999 }1000 1001 1002 /**1003 * Updates the group settings for the logger instance using the specified1004 * specification string.1005 *1006 * @returns iprt status code.1007 * Failures can safely be ignored.1008 * @param pLogger Logger instance.1009 * @param pszVar Value to parse.1010 */1011 RTDECL(int) RTLogGroupSettings(PRTLOGGER pLogger, const char *pszVar)1012 {1013 /*1014 * Resolve defaults.1015 */1016 if (!pLogger)1017 {1018 pLogger = RTLogDefaultInstance();1019 if (!pLogger)1020 return VINF_SUCCESS;1021 }1022 1023 /*1024 * Iterate the string.1025 */1026 while (*pszVar)1027 {1028 /*1029 * Skip prefixes (blanks, ;, + and -).1030 */1031 bool fEnabled = true;1032 char ch;1033 const char *pszStart;1034 unsigned i;1035 size_t cch;1036 1037 while ((ch = *pszVar) == '+' || ch == '-' || ch == ' ' || ch == '\t' || ch == '\n' || ch == ';')1038 {1039 if (ch == '+' || ch == '-' || ';')1040 fEnabled = ch != '-';1041 pszVar++;1042 }1043 if (!*pszVar)1044 break;1045 1046 /*1047 * Find end.1048 */1049 pszStart = pszVar;1050 while ((ch = *pszVar) != '\0' && ch != '+' && ch != '-' && ch != ' ' && ch != '\t')1051 pszVar++;1052 1053 /*1054 * Find the group (ascii case insensitive search).1055 * Special group 'all'.1056 */1057 cch = pszVar - pszStart;1058 if ( cch >= 31059 && (pszStart[0] == 'a' || pszStart[0] == 'A')1060 && (pszStart[1] == 'l' || pszStart[1] == 'L')1061 && (pszStart[2] == 'l' || pszStart[2] == 'L')1062 && (cch == 3 || pszStart[3] == '.' || pszStart[3] == '='))1063 {1064 /*1065 * All.1066 */1067 unsigned fFlags = cch == 31068 ? RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_11069 : rtlogGroupFlags(&pszStart[3]);1070 for (i = 0; i < pLogger->cGroups; i++)1071 {1072 if (fEnabled)1073 pLogger->afGroups[i] |= fFlags;1074 else1075 pLogger->afGroups[i] &= ~fFlags;1076 }1077 }1078 else1079 {1080 /*1081 * Specific group(s).1082 */1083 for (i = 0; i < pLogger->cGroups; i++)1084 {1085 const char *psz2 = (const char*)pszStart;1086 if (rtlogIsGroupMatching(pLogger->papszGroups[i], &psz2, cch))1087 {1088 unsigned fFlags = RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1;1089 if (*psz2 == '.' || *psz2 == '=')1090 fFlags = rtlogGroupFlags(psz2);1091 if (fEnabled)1092 pLogger->afGroups[i] |= fFlags;1093 else1094 pLogger->afGroups[i] &= ~fFlags;1095 }1096 } /* for each group */1097 }1098 1099 } /* parse specification */1100 1101 return VINF_SUCCESS;1102 }1103 1104 1105 /**1106 * Interprets the group flags suffix.1107 *1108 * @returns Flags specified. (0 is possible!)1109 * @param psz Start of Suffix. (Either dot or equal sign.)1110 */1111 static unsigned rtlogGroupFlags(const char *psz)1112 {1113 unsigned fFlags = 0;1114 1115 /*1116 * Litteral flags.1117 */1118 while (*psz == '.')1119 {1120 static struct1121 {1122 const char *pszFlag; /* lowercase!! */1123 unsigned fFlag;1124 } aFlags[] =1125 {1126 { "eo", RTLOGGRPFLAGS_ENABLED },1127 { "enabledonly",RTLOGGRPFLAGS_ENABLED },1128 { "e", RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1 },1129 { "enabled", RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1 },1130 { "l1", RTLOGGRPFLAGS_LEVEL_1 },1131 { "level1", RTLOGGRPFLAGS_LEVEL_1 },1132 { "l", RTLOGGRPFLAGS_LEVEL_2 },1133 { "l2", RTLOGGRPFLAGS_LEVEL_2 },1134 { "level2", RTLOGGRPFLAGS_LEVEL_2 },1135 { "l3", RTLOGGRPFLAGS_LEVEL_3 },1136 { "level3", RTLOGGRPFLAGS_LEVEL_3 },1137 { "l4", RTLOGGRPFLAGS_LEVEL_4 },1138 { "level4", RTLOGGRPFLAGS_LEVEL_4 },1139 { "l5", RTLOGGRPFLAGS_LEVEL_5 },1140 { "level5", RTLOGGRPFLAGS_LEVEL_5 },1141 { "l6", RTLOGGRPFLAGS_LEVEL_6 },1142 { "level6", RTLOGGRPFLAGS_LEVEL_6 },1143 { "f", RTLOGGRPFLAGS_FLOW },1144 { "flow", RTLOGGRPFLAGS_FLOW },1145 1146 { "lelik", RTLOGGRPFLAGS_LELIK },1147 { "michael", RTLOGGRPFLAGS_MICHAEL },1148 { "dmik", RTLOGGRPFLAGS_DMIK },1149 { "sunlover", RTLOGGRPFLAGS_SUNLOVER },1150 { "achim", RTLOGGRPFLAGS_ACHIM },1151 { "achimha", RTLOGGRPFLAGS_ACHIM },1152 { "s", RTLOGGRPFLAGS_SANDER },1153 { "sander", RTLOGGRPFLAGS_SANDER },1154 { "sandervl", RTLOGGRPFLAGS_SANDER },1155 { "klaus", RTLOGGRPFLAGS_KLAUS },1156 { "frank", RTLOGGRPFLAGS_FRANK },1157 { "b", RTLOGGRPFLAGS_BIRD },1158 { "bird", RTLOGGRPFLAGS_BIRD },1159 { "aleksey", RTLOGGRPFLAGS_ALEKSEY },1160 { "n", RTLOGGRPFLAGS_NONAME },1161 { "noname", RTLOGGRPFLAGS_NONAME }1162 };1163 unsigned i;1164 bool fFound = false;1165 psz++;1166 for (i = 0; i < ELEMENTS(aFlags) && !fFound; i++)1167 {1168 const char *psz1 = aFlags[i].pszFlag;1169 const char *psz2 = psz;1170 while (*psz1 == RT_C_TO_LOWER(*psz2))1171 {1172 psz1++;1173 psz2++;1174 if (!*psz1)1175 {1176 if ( (*psz2 >= 'a' && *psz2 <= 'z')1177 || (*psz2 >= 'A' && *psz2 <= 'Z')1178 || (*psz2 >= '0' && *psz2 <= '9') )1179 break;1180 fFlags |= aFlags[i].fFlag;1181 fFound = true;1182 psz = psz2;1183 break;1184 }1185 } /* strincmp */1186 } /* for each flags */1187 }1188 1189 /*1190 * Flag value.1191 */1192 if (*psz == '=')1193 {1194 psz++;1195 if (*psz == '~')1196 fFlags = ~RTStrToInt32(psz + 1);1197 else1198 fFlags = RTStrToInt32(psz);1199 }1200 1201 return fFlags;1202 }1203 1204 #endif /* !IN_GC */1205 1206 1207 /**1208 * Updates the flags for the logger instance using the specified1209 * specification string.1210 *1211 * @returns iprt status code.1212 * Failures can safely be ignored.1213 * @param pLogger Logger instance (NULL for default logger).1214 * @param pszVar Value to parse.1215 */1216 RTDECL(int) RTLogFlags(PRTLOGGER pLogger, const char *pszVar)1217 {1218 int rc = VINF_SUCCESS;1219 1220 /*1221 * Resolve defaults.1222 */1223 if (!pLogger)1224 {1225 pLogger = RTLogDefaultInstance();1226 if (!pLogger)1227 return VINF_SUCCESS;1228 }1229 1230 /*1231 * Iterate the string.1232 */1233 while (*pszVar)1234 {1235 /* parse instruction. */1236 static struct1237 {1238 const char *pszInstr;1239 size_t cchInstr;1240 unsigned fFlag;1241 bool fInverted;1242 } const aDest[] =1243 {1244 { "disabled", sizeof("disabled" ) - 1, RTLOGFLAGS_DISABLED, false },1245 { "enabled", sizeof("enabled" ) - 1, RTLOGFLAGS_DISABLED, true },1246 { "buffered", sizeof("buffered" ) - 1, RTLOGFLAGS_BUFFERED, false },1247 { "unbuffered", sizeof("unbuffered" ) - 1, RTLOGFLAGS_BUFFERED, true },1248 { "usecrlf", sizeof("usecrlf" ) - 1, RTLOGFLAGS_USECRLF, true },1249 { "uself", sizeof("uself" ) - 1, RTLOGFLAGS_USECRLF, false },1250 { "append", sizeof("append" ) - 1, RTLOGFLAGS_APPEND, false },1251 { "overwrite", sizeof("overwrite" ) - 1, RTLOGFLAGS_APPEND, true },1252 { "rel", sizeof("rel" ) - 1, RTLOGFLAGS_REL_TS, false },1253 { "abs", sizeof("abs" ) - 1, RTLOGFLAGS_REL_TS, true },1254 { "dec", sizeof("dec" ) - 1, RTLOGFLAGS_DECIMAL_TS, false },1255 { "hex", sizeof("hex" ) - 1, RTLOGFLAGS_DECIMAL_TS, true },1256 { "lockcnts", sizeof("lockcnts" ) - 1, RTLOGFLAGS_PREFIX_LOCK_COUNTS, false },1257 { "cpuid", sizeof("cpuid" ) - 1, RTLOGFLAGS_PREFIX_CPUID, false },1258 { "pid", sizeof("pid" ) - 1, RTLOGFLAGS_PREFIX_PID, false },1259 { "flagno", sizeof("flagno" ) - 1, RTLOGFLAGS_PREFIX_FLAG_NO, false },1260 { "flag", sizeof("flag" ) - 1, RTLOGFLAGS_PREFIX_FLAG, false },1261 { "groupno", sizeof("groupno" ) - 1, RTLOGFLAGS_PREFIX_GROUP_NO, false },1262 { "group", sizeof("group" ) - 1, RTLOGFLAGS_PREFIX_GROUP, false },1263 { "tid", sizeof("tid" ) - 1, RTLOGFLAGS_PREFIX_TID, false },1264 { "thread", sizeof("thread" ) - 1, RTLOGFLAGS_PREFIX_THREAD, false },1265 { "timeprog", sizeof("timeprog" ) - 1, RTLOGFLAGS_PREFIX_TIME_PROG, false },1266 { "time", sizeof("time" ) - 1, RTLOGFLAGS_PREFIX_TIME, false },1267 { "msprog", sizeof("msprog" ) - 1, RTLOGFLAGS_PREFIX_MS_PROG, false },1268 { "tsc", sizeof("tsc" ) - 1, RTLOGFLAGS_PREFIX_TSC, false }, /* before ts! */1269 { "ts", sizeof("ts" ) - 1, RTLOGFLAGS_PREFIX_TS, false },1270 };1271 1272 /* check no prefix. */1273 bool fNo = false;1274 char ch;1275 unsigned i;1276 1277 /* skip blanks. */1278 while (RT_C_IS_SPACE(*pszVar))1279 pszVar++;1280 if (!*pszVar)1281 return rc;1282 1283 while ((ch = *pszVar) != '\0')1284 {1285 if (ch == 'n' && pszVar[1] == 'o')1286 {1287 pszVar += 2;1288 fNo = !fNo;1289 }1290 else if (ch == '+')1291 {1292 pszVar++;1293 fNo = true;1294 }1295 else if (ch == '-' || ch == '!' || ch == '~')1296 {1297 pszVar++;1298 fNo = !fNo;1299 }1300 else1301 break;1302 }1303 1304 /* instruction. */1305 for (i = 0; i < ELEMENTS(aDest); i++)1306 {1307 if (!strncmp(pszVar, aDest[i].pszInstr, aDest[i].cchInstr))1308 {1309 if (fNo == aDest[i].fInverted)1310 pLogger->fFlags |= aDest[i].fFlag;1311 else1312 pLogger->fFlags &= ~aDest[i].fFlag;1313 pszVar += aDest[i].cchInstr;1314 break;1315 }1316 }1317 1318 /* unknown instruction? */1319 if (i >= ELEMENTS(aDest))1320 {1321 AssertMsgFailed(("Invalid flags! unknown instruction %.20s\n", pszVar));1322 pszVar++;1323 }1324 1325 /* skip blanks and delimiters. */1326 while (RT_C_IS_SPACE(*pszVar) || *pszVar == ';')1327 pszVar++;1328 } /* while more environment variable value left */1329 1330 return rc;1331 }1332 1333 1334 /**1335 * Flushes the specified logger.1336 *1337 * @param pLogger The logger instance to flush.1338 * If NULL the default instance is used. The default instance1339 * will not be initialized by this call.1340 */1341 RTDECL(void) RTLogFlush(PRTLOGGER pLogger)1342 {1343 /*1344 * Resolve defaults.1345 */1346 if (!pLogger)1347 {1348 #ifdef IN_GC1349 pLogger = &g_Logger;1350 #else1351 pLogger = g_pLogger;1352 #endif1353 if (!pLogger)1354 return;1355 }1356 1357 /*1358 * Any thing to flush?1359 */1360 if (pLogger->offScratch)1361 {1362 #ifndef IN_GC1363 /*1364 * Acquire logger instance sem.1365 */1366 int rc = rtlogLock(pLogger);1367 if (RT_FAILURE(rc))1368 return;1369 #endif1370 /*1371 * Call worker.1372 */1373 rtlogFlush(pLogger);1374 1375 #ifndef IN_GC1376 /*1377 * Release the semaphore.1378 */1379 rtlogUnlock(pLogger);1380 #endif1381 }1382 }1383 1384 1385 /**1386 * Gets the default logger instance.1387 *1388 * @returns Pointer to default logger instance.1389 * @returns NULL if no default logger instance available.1390 */1391 RTDECL(PRTLOGGER) RTLogDefaultInstance(void)1392 {1393 #ifdef IN_GC1394 return &g_Logger;1395 1396 #else /* !IN_GC */1397 # ifdef IN_RING01398 /*1399 * Check per thread loggers first.1400 */1401 if (g_cPerThreadLoggers)1402 {1403 const RTNATIVETHREAD Self = RTThreadNativeSelf();1404 int32_t i = ELEMENTS(g_aPerThreadLoggers);1405 while (i-- > 0)1406 if (g_aPerThreadLoggers[i].NativeThread == Self)1407 return g_aPerThreadLoggers[i].pLogger;1408 }1409 # endif /* IN_RING0 */1410 1411 /*1412 * If no per thread logger, use the default one.1413 */1414 if (!g_pLogger)1415 g_pLogger = RTLogDefaultInit();1416 return g_pLogger;1417 #endif /* !IN_GC */1418 }1419 1420 1421 #ifndef IN_GC1422 /**1423 * Sets the default logger instance.1424 *1425 * @returns iprt status code.1426 * @param pLogger The new default logger instance.1427 */1428 RTDECL(PRTLOGGER) RTLogSetDefaultInstance(PRTLOGGER pLogger)1429 {1430 return (PRTLOGGER)ASMAtomicXchgPtr((void * volatile *)&g_pLogger, pLogger);1431 }1432 #endif /* !IN_GC */1433 1434 1435 #ifdef IN_RING01436 /**1437 * Changes the default logger instance for the current thread.1438 *1439 * @returns IPRT status code.1440 * @param pLogger The logger instance. Pass NULL for deregistration.1441 * @param uKey Associated key for cleanup purposes. If pLogger is NULL,1442 * all instances with this key will be deregistered. So in1443 * order to only deregister the instance associated with the1444 * current thread use 0.1445 */1446 RTDECL(int) RTLogSetDefaultInstanceThread(PRTLOGGER pLogger, uintptr_t uKey)1447 {1448 int rc;1449 RTNATIVETHREAD Self = RTThreadNativeSelf();1450 if (pLogger)1451 {1452 int32_t i;1453 unsigned j;1454 1455 AssertReturn(pLogger->u32Magic == RTLOGGER_MAGIC, VERR_INVALID_MAGIC);1456 1457 /*1458 * Iterate the table to see if there is already an entry for this thread.1459 */1460 i = ELEMENTS(g_aPerThreadLoggers);1461 while (i-- > 0)1462 if (g_aPerThreadLoggers[i].NativeThread == Self)1463 {1464 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, (void *)uKey);1465 g_aPerThreadLoggers[i].pLogger = pLogger;1466 return VINF_SUCCESS;1467 }1468 1469 /*1470 * Allocate a new table entry.1471 */1472 i = ASMAtomicIncS32(&g_cPerThreadLoggers);1473 if (i > (int32_t)ELEMENTS(g_aPerThreadLoggers))1474 {1475 ASMAtomicDecS32(&g_cPerThreadLoggers);1476 return VERR_BUFFER_OVERFLOW; /* horrible error code! */1477 }1478 1479 for (j = 0; j < 10; j++)1480 {1481 i = ELEMENTS(g_aPerThreadLoggers);1482 while (i-- > 0)1483 {1484 AssertCompile(sizeof(RTNATIVETHREAD) == sizeof(void*));1485 if ( g_aPerThreadLoggers[i].NativeThread == NIL_RTNATIVETHREAD1486 && ASMAtomicCmpXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].NativeThread, (void *)Self, (void *)NIL_RTNATIVETHREAD))1487 {1488 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, (void *)uKey);1489 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].pLogger, pLogger);1490 return VINF_SUCCESS;1491 }1492 }1493 }1494 1495 ASMAtomicDecS32(&g_cPerThreadLoggers);1496 rc = VERR_INTERNAL_ERROR;1497 }1498 else1499 {1500 /*1501 * Search the array for the current thread.1502 */1503 int32_t i = ELEMENTS(g_aPerThreadLoggers);1504 while (i-- > 0)1505 if ( g_aPerThreadLoggers[i].NativeThread == Self1506 || g_aPerThreadLoggers[i].uKey == uKey)1507 {1508 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, NULL);1509 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].pLogger, NULL);1510 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].NativeThread, (void *)NIL_RTNATIVETHREAD);1511 ASMAtomicDecS32(&g_cPerThreadLoggers);1512 }1513 1514 rc = VINF_SUCCESS;1515 }1516 return rc;1517 }1518 #endif1519 1520 1521 /**1522 * Gets the default release logger instance.1523 *1524 * @returns Pointer to default release logger instance.1525 * @returns NULL if no default release logger instance available.1526 */1527 RTDECL(PRTLOGGER) RTLogRelDefaultInstance(void)1528 {1529 #ifdef IN_GC1530 return &g_RelLogger;1531 #else /* !IN_GC */1532 return g_pRelLogger;1533 #endif /* !IN_GC */1534 }1535 1536 1537 #ifndef IN_GC1538 /**1539 * Sets the default logger instance.1540 *1541 * @returns iprt status code.1542 * @param pLogger The new default release logger instance.1543 */1544 RTDECL(PRTLOGGER) RTLogRelSetDefaultInstance(PRTLOGGER pLogger)1545 {1546 return (PRTLOGGER)ASMAtomicXchgPtr((void * volatile *)&g_pRelLogger, pLogger);1547 }1548 #endif /* !IN_GC */1549 37 1550 38 … … 1576 64 * Write to a logger instance. 1577 65 * 1578 * @param pLogger Pointer to logger instance.1579 * @param pszFormat Format string.1580 * @param args Format arguments.1581 */1582 RTDECL(void) RTLogLoggerV(PRTLOGGER pLogger, const char *pszFormat, va_list args)1583 {1584 RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);1585 }1586 1587 1588 /**1589 * Write to a logger instance.1590 *1591 66 * This function will check whether the instance, group and flags makes up a 1592 67 * logging kind which is currently enabled before writing anything to the log. … … 1611 86 1612 87 /** 1613 * Write to a logger instance.1614 *1615 * This function will check whether the instance, group and flags makes up a1616 * logging kind which is currently enabled before writing anything to the log.1617 *1618 * @param pLogger Pointer to logger instance. If NULL the default logger instance will be attempted.1619 * @param fFlags The logging flags.1620 * @param iGroup The group.1621 * The value ~0U is reserved for compatability with RTLogLogger[V] and is1622 * only for internal usage!1623 * @param pszFormat Format string.1624 * @param args Format arguments.1625 */1626 RTDECL(void) RTLogLoggerExV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)1627 {1628 /*1629 * A NULL logger means default instance.1630 */1631 if (!pLogger)1632 {1633 pLogger = RTLogDefaultInstance();1634 if (!pLogger)1635 return;1636 }1637 rtlogLogger(pLogger, fFlags, iGroup, pszFormat, args);1638 }1639 1640 1641 /**1642 * Write to a logger instance, defaulting to the release one.1643 *1644 * This function will check whether the instance, group and flags makes up a1645 * logging kind which is currently enabled before writing anything to the log.1646 *1647 * @param pLogger Pointer to logger instance.1648 * @param fFlags The logging flags.1649 * @param iGroup The group.1650 * The value ~0U is reserved for compatability with RTLogLogger[V] and is1651 * only for internal usage!1652 * @param pszFormat Format string.1653 * @param ... Format arguments.1654 * @remark This is a worker function for LogRelIt.1655 */1656 RTDECL(void) RTLogRelLogger(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, ...)1657 {1658 va_list args;1659 va_start(args, pszFormat);1660 RTLogRelLoggerV(pLogger, fFlags, iGroup, pszFormat, args);1661 va_end(args);1662 }1663 1664 1665 /**1666 * Write to a logger instance, defaulting to the release one.1667 *1668 * This function will check whether the instance, group and flags makes up a1669 * logging kind which is currently enabled before writing anything to the log.1670 *1671 * @param pLogger Pointer to logger instance. If NULL the default release instance is attempted.1672 * @param fFlags The logging flags.1673 * @param iGroup The group.1674 * The value ~0U is reserved for compatability with RTLogLogger[V] and is1675 * only for internal usage!1676 * @param pszFormat Format string.1677 * @param args Format arguments.1678 */1679 RTDECL(void) RTLogRelLoggerV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)1680 {1681 /*1682 * A NULL logger means default instance.1683 */1684 if (!pLogger)1685 {1686 pLogger = RTLogRelDefaultInstance();1687 if (!pLogger)1688 return;1689 }1690 rtlogLogger(pLogger, fFlags, iGroup, pszFormat, args);1691 }1692 1693 1694 /**1695 * Worker for the RTLog[Rel]Logger*() functions.1696 *1697 * @param pLogger Pointer to logger instance.1698 * @param fFlags The logging flags.1699 * @param iGroup The group.1700 * The value ~0U is reserved for compatability with RTLogLogger[V] and is1701 * only for internal usage!1702 * @param pszFormat Format string.1703 * @param args Format arguments.1704 */1705 static void rtlogLogger(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)1706 {1707 int rc;1708 1709 /*1710 * Validate and correct iGroup.1711 */1712 if (iGroup != ~0U && iGroup >= pLogger->cGroups)1713 iGroup = 0;1714 1715 /*1716 * If no output, then just skip it.1717 */1718 if ( (pLogger->fFlags & RTLOGFLAGS_DISABLED)1719 #ifndef IN_GC1720 || !pLogger->fDestFlags1721 #endif1722 || !pszFormat || !*pszFormat)1723 return;1724 if ( iGroup != ~0U1725 && (pLogger->afGroups[iGroup] & (fFlags | RTLOGGRPFLAGS_ENABLED)) != (fFlags | RTLOGGRPFLAGS_ENABLED))1726 return;1727 1728 /*1729 * Acquire logger instance sem.1730 */1731 rc = rtlogLock(pLogger);1732 if (RT_FAILURE(rc))1733 return;1734 1735 /*1736 * Format the message and perhaps flush it.1737 */1738 if (pLogger->fFlags & (RTLOGFLAGS_PREFIX_MASK | RTLOGFLAGS_USECRLF))1739 {1740 RTLOGOUTPUTPREFIXEDARGS OutputArgs;1741 OutputArgs.pLogger = pLogger;1742 OutputArgs.iGroup = iGroup;1743 OutputArgs.fFlags = fFlags;1744 RTLogFormatV(rtLogOutputPrefixed, &OutputArgs, pszFormat, args);1745 }1746 else1747 RTLogFormatV(rtLogOutput, pLogger, pszFormat, args);1748 if ( !(pLogger->fFlags & RTLOGFLAGS_BUFFERED)1749 && pLogger->offScratch)1750 rtlogFlush(pLogger);1751 1752 /*1753 * Release the semaphore.1754 */1755 rtlogUnlock(pLogger);1756 }1757 1758 1759 /**1760 88 * printf like function for writing to the default log. 1761 89 * … … 1773 101 } 1774 102 1775 1776 /**1777 * vprintf like function for writing to the default log.1778 *1779 * @param pszFormat Printf like format string.1780 * @param args Optional arguments as specified in pszFormat.1781 *1782 * @remark The API doesn't support formatting of floating point numbers at the moment.1783 */1784 RTDECL(void) RTLogPrintfV(const char *pszFormat, va_list args)1785 {1786 RTLogLoggerV(NULL, pszFormat, args);1787 }1788 1789 1790 /**1791 * printf like function for writing to the default release log.1792 *1793 * @param pszFormat Printf like format string.1794 * @param ... Optional arguments as specified in pszFormat.1795 *1796 * @remark The API doesn't support formatting of floating point numbers at the moment.1797 */1798 RTDECL(void) RTLogRelPrintf(const char *pszFormat, ...)1799 {1800 va_list args;1801 va_start(args, pszFormat);1802 RTLogRelPrintfV(pszFormat, args);1803 va_end(args);1804 }1805 1806 1807 /**1808 * vprintf like function for writing to the default release log.1809 *1810 * @param pszFormat Printf like format string.1811 * @param args Optional arguments as specified in pszFormat.1812 *1813 * @remark The API doesn't support formatting of floating point numbers at the moment.1814 */1815 RTDECL(void) RTLogRelPrintfV(const char *pszFormat, va_list args)1816 {1817 RTLogRelLoggerV(NULL, 0, ~0U, pszFormat, args);1818 }1819 1820 1821 /**1822 * Writes the buffer to the given log device without checking for buffered1823 * data or anything.1824 * Used by the RTLogFlush() function.1825 *1826 * @param pLogger The logger instance to write to. NULL is not allowed!1827 */1828 static void rtlogFlush(PRTLOGGER pLogger)1829 {1830 #ifndef IN_GC1831 if (pLogger->fDestFlags & RTLOGDEST_USER)1832 RTLogWriteUser(pLogger->achScratch, pLogger->offScratch);1833 1834 if (pLogger->fDestFlags & RTLOGDEST_DEBUGGER)1835 RTLogWriteDebugger(pLogger->achScratch, pLogger->offScratch);1836 1837 # ifdef IN_RING31838 if (pLogger->fDestFlags & RTLOGDEST_FILE)1839 RTFileWrite(pLogger->File, pLogger->achScratch, pLogger->offScratch, NULL);1840 # endif1841 1842 if (pLogger->fDestFlags & RTLOGDEST_STDOUT)1843 RTLogWriteStdOut(pLogger->achScratch, pLogger->offScratch);1844 1845 if (pLogger->fDestFlags & RTLOGDEST_STDERR)1846 RTLogWriteStdErr(pLogger->achScratch, pLogger->offScratch);1847 1848 # if (defined(IN_RING0) || defined(IN_GC)) && !defined(LOG_NO_COM)1849 if (pLogger->fDestFlags & RTLOGDEST_COM)1850 RTLogWriteCom(pLogger->achScratch, pLogger->offScratch);1851 # endif1852 #endif /* !IN_GC */1853 1854 if (pLogger->pfnFlush)1855 pLogger->pfnFlush(pLogger);1856 1857 /* empty the buffer. */1858 pLogger->offScratch = 0;1859 }1860 1861 1862 /**1863 * Callback for RTLogFormatV which writes to the com port.1864 * See PFNLOGOUTPUT() for details.1865 */1866 static DECLCALLBACK(size_t) rtLogOutput(void *pv, const char *pachChars, size_t cbChars)1867 {1868 PRTLOGGER pLogger = (PRTLOGGER)pv;1869 if (cbChars)1870 {1871 size_t cbRet = 0;1872 for (;;)1873 {1874 #if defined(DEBUG) && defined(IN_RING3)1875 /* sanity */1876 if (pLogger->offScratch >= sizeof(pLogger->achScratch))1877 {1878 fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",1879 pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));1880 AssertBreakpoint(); AssertBreakpoint();1881 }1882 #endif1883 1884 /* how much */1885 size_t cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;1886 if (cb > cbChars)1887 cb = cbChars;1888 1889 /* copy */1890 memcpy(&pLogger->achScratch[pLogger->offScratch], pachChars, cb);1891 1892 /* advance */1893 pLogger->offScratch += cb;1894 cbRet += cb;1895 cbChars -= cb;1896 1897 /* done? */1898 if (cbChars <= 0)1899 return cbRet;1900 1901 pachChars += cb;1902 1903 /* flush */1904 rtlogFlush(pLogger);1905 }1906 1907 /* won't ever get here! */1908 }1909 else1910 {1911 /*1912 * Termination call.1913 * There's always space for a terminator, and it's not counted.1914 */1915 pLogger->achScratch[pLogger->offScratch] = '\0';1916 return 0;1917 }1918 }1919 1920 1921 1922 /**1923 * Callback for RTLogFormatV which writes to the logger instance.1924 * This version supports prefixes.1925 *1926 * See PFNLOGOUTPUT() for details.1927 */1928 static DECLCALLBACK(size_t) rtLogOutputPrefixed(void *pv, const char *pachChars, size_t cbChars)1929 {1930 PRTLOGOUTPUTPREFIXEDARGS pArgs = (PRTLOGOUTPUTPREFIXEDARGS)pv;1931 PRTLOGGER pLogger = pArgs->pLogger;1932 if (cbChars)1933 {1934 size_t cbRet = 0;1935 for (;;)1936 {1937 size_t cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;1938 char *psz;1939 const char *pszNewLine;1940 1941 /*1942 * Pending prefix?1943 */1944 if (pLogger->fPendingPrefix)1945 {1946 pLogger->fPendingPrefix = false;1947 1948 #if defined(DEBUG) && defined(IN_RING3)1949 /* sanity */1950 if (pLogger->offScratch >= sizeof(pLogger->achScratch))1951 {1952 fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",1953 pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));1954 AssertBreakpoint(); AssertBreakpoint();1955 }1956 #endif1957 1958 /*1959 * Flush the buffer if there isn't enough room for the maximum prefix config.1960 * Max is 224, add a couple of extra bytes.1961 */1962 if (cb < 224 + 16)1963 {1964 rtlogFlush(pLogger);1965 cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;1966 }1967 1968 /*1969 * Write the prefixes.1970 * psz is pointing to the current position.1971 */1972 psz = &pLogger->achScratch[pLogger->offScratch];1973 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TS)1974 {1975 #if defined(IN_RING3) || defined(IN_GC)1976 uint64_t u64 = RTTimeNanoTS();1977 #else1978 uint64_t u64 = ~0;1979 #endif1980 int iBase = 16;1981 unsigned int fFlags = RTSTR_F_ZEROPAD;1982 if (pLogger->fFlags & RTLOGFLAGS_DECIMAL_TS)1983 {1984 iBase = 10;1985 fFlags = 0;1986 }1987 if (pLogger->fFlags & RTLOGFLAGS_REL_TS)1988 {1989 static volatile uint64_t s_u64LastTs;1990 uint64_t u64DiffTs = u64 - s_u64LastTs;1991 s_u64LastTs = u64;1992 /* We could have been preempted just before reading of s_u64LastTs by1993 * another thread which wrote s_u64LastTs. In that case the difference1994 * is negative which we simply ignore. */1995 u64 = (int64_t)u64DiffTs < 0 ? 0 : u64DiffTs;1996 }1997 /* 1E15 nanoseconds = 11 days */1998 psz += RTStrFormatNumber(psz, u64, iBase, 16, 0, fFlags); /* +17 */1999 *psz++ = ' ';2000 }2001 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TSC)2002 {2003 uint64_t u64 = ASMReadTSC();2004 int iBase = 16;2005 unsigned int fFlags = RTSTR_F_ZEROPAD;2006 if (pLogger->fFlags & RTLOGFLAGS_DECIMAL_TS)2007 {2008 iBase = 10;2009 fFlags = 0;2010 }2011 if (pLogger->fFlags & RTLOGFLAGS_REL_TS)2012 {2013 static volatile uint64_t s_u64LastTsc;2014 uint64_t u64DiffTsc = u64 - s_u64LastTsc;2015 s_u64LastTsc = u64;2016 /* We could have been preempted just before reading of s_u64LastTsc by2017 * another thread which wrote s_u64LastTsc. In that case the difference2018 * is negative which we simply ignore. */2019 u64 = u64DiffTsc < 0 ? 0 : u64DiffTsc;2020 }2021 /* 1E15 ticks at 4GHz = 69 hours */2022 psz += RTStrFormatNumber(psz, u64, iBase, 16, 0, fFlags); /* +17 */2023 *psz++ = ' ';2024 }2025 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_MS_PROG)2026 {2027 #if defined(IN_RING3) || defined(IN_GC)2028 uint64_t u64 = RTTimeProgramMilliTS();2029 #else2030 uint64_t u64 = 0;2031 #endif2032 /* 1E8 milliseconds = 27 hours */2033 psz += RTStrFormatNumber(psz, u64, 10, 9, 0, RTSTR_F_ZEROPAD);2034 *psz++ = ' ';2035 }2036 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TIME)2037 {2038 #ifdef IN_RING32039 RTTIMESPEC TimeSpec;2040 RTTIME Time;2041 RTTimeExplode(&Time, RTTimeNow(&TimeSpec));2042 psz += RTStrFormatNumber(psz, Time.u8Hour, 10, 2, 0, RTSTR_F_ZEROPAD);2043 *psz++ = ':';2044 psz += RTStrFormatNumber(psz, Time.u8Minute, 10, 2, 0, RTSTR_F_ZEROPAD);2045 *psz++ = ':';2046 psz += RTStrFormatNumber(psz, Time.u8Second, 10, 2, 0, RTSTR_F_ZEROPAD);2047 *psz++ = '.';2048 psz += RTStrFormatNumber(psz, Time.u32Nanosecond / 1000000, 10, 3, 0, RTSTR_F_ZEROPAD);2049 *psz++ = ' '; /* +17 (3+1+3+1+3+1+4+1) */2050 #else2051 memset(psz, ' ', 13);2052 psz += 13;2053 #endif2054 }2055 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TIME_PROG)2056 {2057 #ifdef IN_RING32058 uint64_t u64 = RTTimeProgramMilliTS();2059 psz += RTStrFormatNumber(psz, (uint32_t)(u64 / (60 * 60 * 1000)), 10, 2, 0, RTSTR_F_ZEROPAD);2060 *psz++ = ':';2061 uint32_t u32 = (uint32_t)(u64 % (60 * 60 * 1000));2062 psz += RTStrFormatNumber(psz, u32 / (60 * 1000), 10, 2, 0, RTSTR_F_ZEROPAD);2063 *psz++ = ':';2064 u32 %= 60 * 1000;2065 psz += RTStrFormatNumber(psz, u32 / 1000, 10, 2, 0, RTSTR_F_ZEROPAD);2066 *psz++ = '.';2067 psz += RTStrFormatNumber(psz, u32 % 1000, 10, 3, 0, RTSTR_F_ZEROPAD);2068 *psz++ = ' '; /* +20 (9+1+2+1+2+1+3+1) */2069 #else2070 memset(psz, ' ', 13);2071 psz += 13;2072 #endif2073 }2074 # if 02075 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_DATETIME)2076 {2077 char szDate[32];2078 RTTIMESPEC Time;2079 RTTimeSpecToString(RTTimeNow(&Time), szDate, sizeof(szDate));2080 size_t cch = strlen(szDate);2081 memcpy(psz, szDate, cch);2082 psz += cch;2083 *psz++ = ' '; /* +32 */2084 }2085 # endif2086 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_PID)2087 {2088 #ifndef IN_GC2089 RTPROCESS Process = RTProcSelf();2090 #else2091 RTPROCESS Process = NIL_RTPROCESS;2092 #endif2093 psz += RTStrFormatNumber(psz, Process, 16, sizeof(RTPROCESS) * 2, 0, RTSTR_F_ZEROPAD);2094 *psz++ = ' '; /* +9 */2095 }2096 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TID)2097 {2098 #ifndef IN_GC2099 RTNATIVETHREAD Thread = RTThreadNativeSelf();2100 #else2101 RTNATIVETHREAD Thread = NIL_RTNATIVETHREAD;2102 #endif2103 psz += RTStrFormatNumber(psz, Thread, 16, sizeof(RTNATIVETHREAD) * 2, 0, RTSTR_F_ZEROPAD);2104 *psz++ = ' '; /* +17 */2105 }2106 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_THREAD)2107 {2108 #ifdef IN_RING32109 const char *pszName = RTThreadSelfName();2110 #elif defined IN_GC2111 const char *pszName = "EMT-GC";2112 #else2113 const char *pszName = "EMT-R0";2114 #endif2115 size_t cch = 0;2116 if (pszName)2117 {2118 cch = strlen(pszName);2119 cch = RT_MIN(cch, 16);2120 memcpy(psz, pszName, cch);2121 psz += cch;2122 }2123 do2124 *psz++ = ' ';2125 while (cch++ < 8); /* +17 */2126 }2127 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_CPUID)2128 {2129 #if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)2130 const uint8_t idCpu = ASMGetApicId();2131 #else2132 const RTCPUID idCpu = RTMpCpuId();2133 #endif2134 psz += RTStrFormatNumber(psz, idCpu, 16, sizeof(idCpu) * 2, 0, RTSTR_F_ZEROPAD);2135 *psz++ = ' '; /* +17 */2136 }2137 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_LOCK_COUNTS)2138 {2139 #ifdef IN_RING3 /** @todo implement these counters in ring-0 too? */2140 RTTHREAD Thread = RTThreadSelf();2141 if (Thread != NIL_RTTHREAD)2142 {2143 uint32_t cReadLocks = RTThreadGetReadLockCount(Thread);2144 uint32_t cWriteLocks = RTThreadGetWriteLockCount(Thread) - g_cLoggerLockCount;2145 cReadLocks = RT_MIN(0xfff, cReadLocks);2146 cWriteLocks = RT_MIN(0xfff, cWriteLocks);2147 psz += RTStrFormatNumber(psz, cReadLocks, 16, 1, 0, RTSTR_F_ZEROPAD);2148 *psz++ = '/';2149 psz += RTStrFormatNumber(psz, cWriteLocks, 16, 1, 0, RTSTR_F_ZEROPAD);2150 }2151 else2152 #endif2153 {2154 *psz++ = '?';2155 *psz++ = '/';2156 *psz++ = '?';2157 }2158 *psz++ = ' '; /* +8 */2159 }2160 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_FLAG_NO)2161 {2162 psz += RTStrFormatNumber(psz, pArgs->fFlags, 16, 8, 0, RTSTR_F_ZEROPAD);2163 *psz++ = ' '; /* +9 */2164 }2165 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_FLAG)2166 {2167 #ifdef IN_RING32168 const char *pszGroup = pArgs->iGroup != ~0U ? pLogger->papszGroups[pArgs->iGroup] : NULL;2169 #else2170 const char *pszGroup = NULL;2171 #endif2172 size_t cch = 0;2173 if (pszGroup)2174 {2175 cch = strlen(pszGroup);2176 cch = RT_MIN(cch, 16);2177 memcpy(psz, pszGroup, cch);2178 psz += cch;2179 }2180 do2181 *psz++ = ' ';2182 while (cch++ < 8); /* +17 */2183 }2184 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_GROUP_NO)2185 {2186 if (pArgs->iGroup != ~0U)2187 {2188 psz += RTStrFormatNumber(psz, pArgs->iGroup, 16, 3, 0, RTSTR_F_ZEROPAD);2189 *psz++ = ' ';2190 }2191 else2192 {2193 memcpy(psz, "-1 ", sizeof("-1 ") - 1);2194 psz += sizeof("-1 ") - 1;2195 } /* +9 */2196 }2197 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_GROUP)2198 {2199 const unsigned fGrp = pLogger->afGroups[pArgs->iGroup != ~0U ? pArgs->iGroup : 0];2200 const char *pszGroup;2201 size_t cch;2202 switch (pArgs->fFlags & fGrp)2203 {2204 case 0: pszGroup = "--------"; cch = sizeof("--------") - 1; break;2205 case RTLOGGRPFLAGS_ENABLED: pszGroup = "enabled" ; cch = sizeof("enabled" ) - 1; break;2206 case RTLOGGRPFLAGS_LEVEL_1: pszGroup = "level 1" ; cch = sizeof("level 1" ) - 1; break;2207 case RTLOGGRPFLAGS_LEVEL_2: pszGroup = "level 2" ; cch = sizeof("level 2" ) - 1; break;2208 case RTLOGGRPFLAGS_LEVEL_3: pszGroup = "level 3" ; cch = sizeof("level 3" ) - 1; break;2209 case RTLOGGRPFLAGS_LEVEL_4: pszGroup = "level 4" ; cch = sizeof("level 4" ) - 1; break;2210 case RTLOGGRPFLAGS_LEVEL_5: pszGroup = "level 5" ; cch = sizeof("level 5" ) - 1; break;2211 case RTLOGGRPFLAGS_LEVEL_6: pszGroup = "level 6" ; cch = sizeof("level 6" ) - 1; break;2212 case RTLOGGRPFLAGS_FLOW: pszGroup = "flow" ; cch = sizeof("flow" ) - 1; break;2213 2214 /* personal groups */2215 case RTLOGGRPFLAGS_LELIK: pszGroup = "lelik" ; cch = sizeof("lelik" ) - 1; break;2216 case RTLOGGRPFLAGS_MICHAEL: pszGroup = "Michael" ; cch = sizeof("Michael" ) - 1; break;2217 case RTLOGGRPFLAGS_DMIK: pszGroup = "dmik" ; cch = sizeof("dmik" ) - 1; break;2218 case RTLOGGRPFLAGS_SUNLOVER: pszGroup = "sunlover"; cch = sizeof("sunlover") - 1; break;2219 case RTLOGGRPFLAGS_ACHIM: pszGroup = "Achim" ; cch = sizeof("Achim" ) - 1; break;2220 case RTLOGGRPFLAGS_SANDER: pszGroup = "Sander" ; cch = sizeof("Sander" ) - 1; break;2221 case RTLOGGRPFLAGS_KLAUS: pszGroup = "Klaus" ; cch = sizeof("Klaus" ) - 1; break;2222 case RTLOGGRPFLAGS_FRANK: pszGroup = "Frank" ; cch = sizeof("Frank" ) - 1; break;2223 case RTLOGGRPFLAGS_BIRD: pszGroup = "bird" ; cch = sizeof("bird" ) - 1; break;2224 case RTLOGGRPFLAGS_NONAME: pszGroup = "noname" ; cch = sizeof("noname" ) - 1; break;2225 default: pszGroup = "????????"; cch = sizeof("????????") - 1; break;2226 }2227 if (pszGroup)2228 {2229 cch = RT_MIN(cch, 16);2230 memcpy(psz, pszGroup, cch);2231 psz += cch;2232 }2233 do2234 *psz++ = ' ';2235 while (cch++ < 8); /* +17 */2236 }2237 2238 /*2239 * Done, figure what we've used and advance the buffer and free size.2240 */2241 cb = psz - &pLogger->achScratch[pLogger->offScratch];2242 Assert(cb <= 198);2243 pLogger->offScratch += cb;2244 cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;2245 }2246 else if (cb <= 0)2247 {2248 rtlogFlush(pLogger);2249 cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;2250 }2251 2252 #if defined(DEBUG) && defined(IN_RING3)2253 /* sanity */2254 if (pLogger->offScratch >= sizeof(pLogger->achScratch))2255 {2256 fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",2257 pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));2258 AssertBreakpoint(); AssertBreakpoint();2259 }2260 #endif2261 2262 /* how much */2263 if (cb > cbChars)2264 cb = cbChars;2265 2266 /* have newline? */2267 pszNewLine = (const char *)memchr(pachChars, '\n', cb);2268 if (pszNewLine)2269 {2270 if (pLogger->fFlags & RTLOGFLAGS_USECRLF)2271 cb = pszNewLine - pachChars;2272 else2273 {2274 cb = pszNewLine - pachChars + 1;2275 pLogger->fPendingPrefix = true;2276 }2277 }2278 2279 /* copy */2280 memcpy(&pLogger->achScratch[pLogger->offScratch], pachChars, cb);2281 2282 /* advance */2283 pLogger->offScratch += cb;2284 cbRet += cb;2285 cbChars -= cb;2286 2287 if ( pszNewLine2288 && (pLogger->fFlags & RTLOGFLAGS_USECRLF)2289 && pLogger->offScratch + 2 < sizeof(pLogger->achScratch))2290 {2291 memcpy(&pLogger->achScratch[pLogger->offScratch], "\r\n", 2);2292 pLogger->offScratch += 2;2293 cbRet++;2294 cbChars--;2295 cb++;2296 pLogger->fPendingPrefix = true;2297 }2298 2299 /* done? */2300 if (cbChars <= 0)2301 return cbRet;2302 pachChars += cb;2303 }2304 2305 /* won't ever get here! */2306 }2307 else2308 {2309 /*2310 * Termination call.2311 * There's always space for a terminator, and it's not counted.2312 */2313 pLogger->achScratch[pLogger->offScratch] = '\0';2314 return 0;2315 }2316 }2317 -
trunk/src/VBox/Runtime/common/log/logrel.cpp
r12036 r12099 61 61 62 62 /******************************************************************************* 63 * Structures and Typedefs *64 *******************************************************************************/65 /**66 * Arguments passed to the output function.67 */68 typedef struct RTLOGOUTPUTPREFIXEDARGS69 {70 /** The logger instance. */71 PRTLOGGER pLogger;72 /** The flags. (used for prefixing.) */73 unsigned fFlags;74 /** The group. (used for prefixing.) */75 unsigned iGroup;76 } RTLOGOUTPUTPREFIXEDARGS, *PRTLOGOUTPUTPREFIXEDARGS;77 78 79 /*******************************************************************************80 * Internal Functions *81 *******************************************************************************/82 #ifndef IN_GC83 static unsigned rtlogGroupFlags(const char *psz);84 #endif85 static void rtlogLogger(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args);86 static void rtlogFlush(PRTLOGGER pLogger);87 static DECLCALLBACK(size_t) rtLogOutput(void *pv, const char *pachChars, size_t cbChars);88 static DECLCALLBACK(size_t) rtLogOutputPrefixed(void *pv, const char *pachChars, size_t cbChars);89 90 91 /*******************************************************************************92 63 * Global Variables * 93 64 *******************************************************************************/ 94 65 #ifdef IN_GC 95 /** Default logger instance. */96 extern "C" DECLIMPORT(RTLOGGERRC) g_Logger;97 66 /** Default relese logger instance. */ 98 67 extern "C" DECLIMPORT(RTLOGGERRC) g_RelLogger; 99 68 #else /* !IN_GC */ 100 /** Default logger instance. */101 static PRTLOGGER g_pLogger;102 69 /** Default release logger instance. */ 103 70 static PRTLOGGER g_pRelLogger; 104 71 #endif /* !IN_GC */ 105 #ifdef IN_RING3106 /** The RTThreadGetWriteLockCount() change caused by the logger mutex semaphore. */107 static uint32_t volatile g_cLoggerLockCount;108 #endif109 #ifdef IN_RING0110 /** Number of per-thread loggers. */111 static int32_t volatile g_cPerThreadLoggers;112 /** Per-thread loggers.113 * This is just a quick TLS hack suitable for debug logging only.114 * If we run out of entries, just unload and reload the driver. */115 static struct RTLOGGERPERTHREAD116 {117 /** The thread. */118 RTNATIVETHREAD volatile NativeThread;119 /** The (process / session) key. */120 uintptr_t volatile uKey;121 /** The logger instance.*/122 PRTLOGGER volatile pLogger;123 } g_aPerThreadLoggers[8] =124 { { NIL_RTNATIVETHREAD, 0, 0},125 { NIL_RTNATIVETHREAD, 0, 0},126 { NIL_RTNATIVETHREAD, 0, 0},127 { NIL_RTNATIVETHREAD, 0, 0},128 { NIL_RTNATIVETHREAD, 0, 0},129 { NIL_RTNATIVETHREAD, 0, 0},130 { NIL_RTNATIVETHREAD, 0, 0},131 { NIL_RTNATIVETHREAD, 0, 0}132 };133 #endif /* IN_RING0 */134 135 136 /**137 * Locks the logger instance.138 *139 * @returns See RTSemFastMutexRequest().140 * @param pLogger The logger instance.141 */142 DECLINLINE(int) rtlogLock(PRTLOGGER pLogger)143 {144 #ifndef IN_GC145 if (pLogger->MutexSem != NIL_RTSEMFASTMUTEX)146 {147 int rc = RTSemFastMutexRequest(pLogger->MutexSem);148 AssertRCReturn(rc, rc);149 }150 #endif151 return VINF_SUCCESS;152 }153 154 155 /**156 * Unlocks the logger instance.157 * @param pLogger The logger instance.158 */159 DECLINLINE(void) rtlogUnlock(PRTLOGGER pLogger)160 {161 #ifndef IN_GC162 if (pLogger->MutexSem != NIL_RTSEMFASTMUTEX)163 RTSemFastMutexRelease(pLogger->MutexSem);164 #endif165 return;166 }167 168 169 #ifndef IN_GC170 /**171 * Create a logger instance, comprehensive version.172 *173 * @returns iprt status code.174 *175 * @param ppLogger Where to store the logger instance.176 * @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.177 * @param pszGroupSettings The initial group settings.178 * @param pszEnvVarBase Base name for the environment variables for this instance.179 * @param cGroups Number of groups in the array.180 * @param papszGroups Pointer to array of groups. This must stick around for the life of the181 * logger instance.182 * @param fDestFlags The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.183 * @param pszErrorMsg A buffer which is filled with an error message if something fails. May be NULL.184 * @param cchErrorMsg The size of the error message buffer.185 * @param pszFilenameFmt Log filename format string. Standard RTStrFormat().186 * @param ... Format arguments.187 */188 RTDECL(int) RTLogCreateExV(PRTLOGGER *ppLogger, RTUINT fFlags, const char *pszGroupSettings,189 const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,190 RTUINT fDestFlags, char *pszErrorMsg, size_t cchErrorMsg, const char *pszFilenameFmt, va_list args)191 {192 int rc;193 size_t cb;194 PRTLOGGER pLogger;195 196 /*197 * Validate input.198 */199 if ( (cGroups && !papszGroups)200 || !VALID_PTR(ppLogger)201 )202 {203 AssertMsgFailed(("Invalid parameters!\n"));204 return VERR_INVALID_PARAMETER;205 }206 *ppLogger = NULL;207 208 if (pszErrorMsg)209 RTStrPrintf(pszErrorMsg, cchErrorMsg, "unknown error");210 211 /*212 * Allocate a logger instance.213 */214 cb = RT_OFFSETOF(RTLOGGER, afGroups[cGroups + 1]) + RTPATH_MAX;215 pLogger = (PRTLOGGER)RTMemAllocZ(cb);216 if (pLogger)217 {218 uint8_t *pu8Code;219 220 pLogger->u32Magic = RTLOGGER_MAGIC;221 pLogger->papszGroups = papszGroups;222 pLogger->cMaxGroups = cGroups;223 pLogger->cGroups = cGroups;224 pLogger->pszFilename = (char *)&pLogger->afGroups[cGroups + 1];225 pLogger->File = NIL_RTFILE;226 pLogger->fFlags = fFlags;227 pLogger->fDestFlags = fDestFlags;228 pLogger->fPendingPrefix = true;229 if (pszGroupSettings)230 RTLogGroupSettings(pLogger, pszGroupSettings);231 232 /*233 * Emit wrapper code.234 */235 pu8Code = (uint8_t *)RTMemExecAlloc(64);236 if (pu8Code)237 {238 pLogger->pfnLogger = *(PFNRTLOGGER*)&pu8Code;239 #ifdef RT_ARCH_AMD64240 /* this wrapper will not be used on AMD64, we will be requiring C99 compilers there. */241 *pu8Code++ = 0xcc;242 #else243 *pu8Code++ = 0x68; /* push imm32 */244 *(void **)pu8Code = pLogger;245 pu8Code += sizeof(void *);246 *pu8Code++ = 0xe8; /* call rel32 */247 *(uint32_t *)pu8Code = (uintptr_t)RTLogLogger - ((uintptr_t)pu8Code + sizeof(uint32_t));248 pu8Code += sizeof(uint32_t);249 *pu8Code++ = 0x8d; /* lea esp, [esp + 4] */250 *pu8Code++ = 0x64;251 *pu8Code++ = 0x24;252 *pu8Code++ = 0x04;253 *pu8Code++ = 0xc3; /* ret near */254 #endif255 AssertMsg((uintptr_t)pu8Code - (uintptr_t)pLogger->pfnLogger <= 64,256 ("Wrapper assembly is too big! %d bytes\n", (uintptr_t)pu8Code - (uintptr_t)pLogger->pfnLogger));257 258 259 #ifdef IN_RING3 /* files and env.vars. are only accessible when in R3 at the present time. */260 /*261 * Format the filename.262 */263 if (pszFilenameFmt)264 {265 RTStrPrintfV(pLogger->pszFilename, RTPATH_MAX, pszFilenameFmt, args);266 pLogger->fDestFlags |= RTLOGDEST_FILE;267 }268 269 /*270 * Parse the environment variables.271 */272 if (pszEnvVarBase)273 {274 /* make temp copy of environment variable base. */275 size_t cchEnvVarBase = strlen(pszEnvVarBase);276 char *pszEnvVar = (char *)alloca(cchEnvVarBase + 16);277 memcpy(pszEnvVar, pszEnvVarBase, cchEnvVarBase);278 279 /*280 * Destination.281 */282 strcpy(pszEnvVar + cchEnvVarBase, "_DEST");283 const char *pszVar = getenv(pszEnvVar);284 if (pszVar)285 {286 while (*pszVar)287 {288 /* skip blanks. */289 while (RT_C_IS_SPACE(*pszVar))290 pszVar++;291 if (!*pszVar)292 break;293 294 /* parse instruction. */295 static struct296 {297 const char *pszInstr;298 unsigned fFlag;299 } const aDest[] =300 {301 { "file", RTLOGDEST_FILE }, /* Must be 1st! */302 { "dir", RTLOGDEST_FILE }, /* Must be 2nd! */303 { "stdout", RTLOGDEST_STDOUT },304 { "stderr", RTLOGDEST_STDERR },305 { "debugger", RTLOGDEST_DEBUGGER },306 { "com", RTLOGDEST_COM },307 { "user", RTLOGDEST_USER },308 };309 310 /* check no prefix. */311 bool fNo = false;312 if (pszVar[0] == 'n' && pszVar[1] == 'o')313 {314 fNo = true;315 pszVar += 2;316 }317 318 /* instruction. */319 unsigned i;320 for (i = 0; i < ELEMENTS(aDest); i++)321 {322 size_t cchInstr = strlen(aDest[i].pszInstr);323 if (!strncmp(pszVar, aDest[i].pszInstr, cchInstr))324 {325 if (!fNo)326 pLogger->fDestFlags |= aDest[i].fFlag;327 else328 pLogger->fDestFlags &= ~aDest[i].fFlag;329 pszVar += cchInstr;330 331 /* check for value. */332 while (RT_C_IS_SPACE(*pszVar))333 pszVar++;334 if (*pszVar == '=' || *pszVar == ':')335 {336 pszVar++;337 const char *pszEnd = strchr(pszVar, ';');338 if (!pszEnd)339 pszEnd = strchr(pszVar, '\0');340 341 /* log file name */342 size_t cch = pszEnd - pszVar;343 if (i == 0 /* file */ && !fNo)344 {345 memcpy(pLogger->pszFilename, pszVar, cch);346 pLogger->pszFilename[cch] = '\0';347 }348 /* log directory */349 else if (i == 1 /* dir */ && !fNo)350 {351 char szTmp[RTPATH_MAX];352 const char *pszFile = RTPathFilename(pLogger->pszFilename);353 if (pszFile)354 strcpy(szTmp, pszFile);355 else356 pszFile = ""; /* you've screwed up, sir. */357 358 memcpy(pLogger->pszFilename, pszVar, cch);359 pLogger->pszFilename[cch] = '\0';360 RTPathStripTrailingSlash(pLogger->pszFilename);361 362 cch = strlen(pLogger->pszFilename);363 pLogger->pszFilename[cch++] = '/';364 strcpy(&pLogger->pszFilename[cch], szTmp);365 }366 else367 AssertMsgFailed(("Invalid %s_DEST! %s%s doesn't take a value!\n", pszEnvVarBase, fNo ? "no" : "", aDest[i].pszInstr));368 pszVar = pszEnd + (*pszEnd != '\0');369 }370 break;371 }372 }373 /* unknown instruction? */374 if (i >= ELEMENTS(aDest))375 {376 AssertMsgFailed(("Invalid %s_DEST! unknown instruction %.20s\n", pszEnvVarBase, pszVar));377 pszVar++;378 }379 380 /* skip blanks and delimiters. */381 while (RT_C_IS_SPACE(*pszVar) || *pszVar == ';')382 pszVar++;383 } /* while more environment variable value left */384 }385 386 /*387 * The flags.388 */389 strcpy(pszEnvVar + cchEnvVarBase, "_FLAGS");390 pszVar = getenv(pszEnvVar);391 if (pszVar)392 RTLogFlags(pLogger, pszVar);393 394 /*395 * The group settings.396 */397 pszEnvVar[cchEnvVarBase] = '\0';398 pszVar = getenv(pszEnvVar);399 if (pszVar)400 RTLogGroupSettings(pLogger, pszVar);401 }402 #endif /* IN_RING3 */403 404 /*405 * Open the destination(s).406 */407 rc = VINF_SUCCESS;408 #ifdef IN_RING3409 if (pLogger->fDestFlags & RTLOGDEST_FILE)410 {411 if (!(pLogger->fFlags & RTLOGFLAGS_APPEND))412 rc = RTFileOpen(&pLogger->File, pLogger->pszFilename,413 RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE);414 else415 {416 /** @todo RTFILE_O_APPEND. */417 rc = RTFileOpen(&pLogger->File, pLogger->pszFilename,418 RTFILE_O_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE);419 if (RT_SUCCESS(rc))420 {421 rc = RTFileSeek(pLogger->File, 0, RTFILE_SEEK_END, NULL);422 if (RT_FAILURE(rc))423 {424 RTFileClose(pLogger->File);425 pLogger->File = NIL_RTFILE;426 }427 }428 }429 if (RT_FAILURE(rc) && pszErrorMsg)430 RTStrPrintf(pszErrorMsg, cchErrorMsg, "could not open file '%s'", pLogger->pszFilename);431 }432 #endif /* IN_RING3 */433 434 /*435 * Create mutex and check how much it counts when entering the lock436 * so that we can report the values for RTLOGFLAGS_PREFIX_LOCK_COUNTS.437 */438 if (RT_SUCCESS(rc))439 {440 rc = RTSemFastMutexCreate(&pLogger->MutexSem);441 if (RT_SUCCESS(rc))442 {443 #ifdef IN_RING3 /** @todo do counters in ring-0 too? */444 RTTHREAD Thread = RTThreadSelf();445 if (Thread != NIL_RTTHREAD)446 {447 int32_t c = RTThreadGetWriteLockCount(Thread);448 RTSemFastMutexRequest(pLogger->MutexSem);449 c = RTThreadGetWriteLockCount(Thread) - c;450 RTSemFastMutexRelease(pLogger->MutexSem);451 ASMAtomicWriteU32(&g_cLoggerLockCount, c);452 }453 #endif454 *ppLogger = pLogger;455 return VINF_SUCCESS;456 }457 458 if (pszErrorMsg)459 RTStrPrintf(pszErrorMsg, cchErrorMsg, "failed to create sempahore");460 }461 #ifdef IN_RING3462 RTFileClose(pLogger->File);463 #endif464 RTMemExecFree(*(void **)&pLogger->pfnLogger);465 }466 else467 {468 #ifdef RT_OS_LINUX469 /*470 * RTMemAlloc() succeeded but RTMemExecAlloc() failed -- most probably an SELinux problem.471 */472 if (pszErrorMsg)473 RTStrPrintf(pszErrorMsg, cchErrorMsg, "mmap(PROT_WRITE | PROT_EXEC) failed -- SELinux?");474 #endif /* RT_OS_LINUX */475 rc = VERR_NO_MEMORY;476 }477 RTMemFree(pLogger);478 }479 else480 rc = VERR_NO_MEMORY;481 482 return rc;483 }484 485 /**486 * Create a logger instance.487 *488 * @returns iprt status code.489 *490 * @param ppLogger Where to store the logger instance.491 * @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.492 * @param pszGroupSettings The initial group settings.493 * @param pszEnvVarBase Base name for the environment variables for this instance.494 * @param cGroups Number of groups in the array.495 * @param papszGroups Pointer to array of groups. This must stick around for the life of the496 * logger instance.497 * @param fDestFlags The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.498 * @param pszFilenameFmt Log filename format string. Standard RTStrFormat().499 * @param ... Format arguments.500 */501 RTDECL(int) RTLogCreate(PRTLOGGER *ppLogger, RTUINT fFlags, const char *pszGroupSettings,502 const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,503 RTUINT fDestFlags, const char *pszFilenameFmt, ...)504 {505 va_list args;506 int rc;507 508 va_start(args, pszFilenameFmt);509 rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups, fDestFlags, NULL, 0, pszFilenameFmt, args);510 va_end(args);511 return rc;512 }513 514 /**515 * Create a logger instance.516 *517 * @returns iprt status code.518 *519 * @param ppLogger Where to store the logger instance.520 * @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.521 * @param pszGroupSettings The initial group settings.522 * @param pszEnvVarBase Base name for the environment variables for this instance.523 * @param cGroups Number of groups in the array.524 * @param papszGroups Pointer to array of groups. This must stick around for the life of the525 * logger instance.526 * @param fDestFlags The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.527 * @param pszErrorMsg A buffer which is filled with an error message if something fails. May be NULL.528 * @param cchErrorMsg The size of the error message buffer.529 * @param pszFilenameFmt Log filename format string. Standard RTStrFormat().530 * @param ... Format arguments.531 */532 RTDECL(int) RTLogCreateEx(PRTLOGGER *ppLogger, RTUINT fFlags, const char *pszGroupSettings,533 const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,534 RTUINT fDestFlags, char *pszErrorMsg, size_t cchErrorMsg, const char *pszFilenameFmt, ...)535 {536 va_list args;537 int rc;538 539 va_start(args, pszFilenameFmt);540 rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups, fDestFlags, pszErrorMsg, cchErrorMsg, pszFilenameFmt, args);541 va_end(args);542 return rc;543 }544 545 /**546 * Destroys a logger instance.547 *548 * The instance is flushed and all output destinations closed (where applicable).549 *550 * @returns iprt status code.551 * @param pLogger The logger instance which close destroyed. NULL is fine.552 */553 RTDECL(int) RTLogDestroy(PRTLOGGER pLogger)554 {555 int rc;556 RTUINT iGroup;557 RTSEMFASTMUTEX MutexSem;558 559 /*560 * Validate input.561 */562 if (!pLogger)563 return VINF_SUCCESS;564 AssertReturn(VALID_PTR(pLogger), VERR_INVALID_POINTER);565 AssertReturn(pLogger->u32Magic == RTLOGGER_MAGIC, VERR_INVALID_MAGIC);566 567 /*568 * Acquire logger instance sem and disable all logging. (paranoia)569 */570 rc = rtlogLock(pLogger);571 if (RT_FAILURE(rc))572 return rc;573 574 pLogger->fFlags |= RTLOGFLAGS_DISABLED;575 iGroup = pLogger->cGroups;576 while (iGroup-- > 0)577 pLogger->afGroups[iGroup] = 0;578 579 /*580 * Flush it.581 */582 RTLogFlush(pLogger);583 584 /*585 * Close output stuffs.586 */587 #ifdef IN_RING3588 if (pLogger->File != NIL_RTFILE)589 {590 int rc2 = RTFileClose(pLogger->File);591 AssertRC(rc2);592 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))593 rc = rc2;594 pLogger->File = NIL_RTFILE;595 }596 #endif597 598 /*599 * Free the mutex and the instance memory.600 */601 MutexSem = pLogger->MutexSem;602 pLogger->MutexSem = NIL_RTSEMFASTMUTEX;603 if (MutexSem != NIL_RTSEMFASTMUTEX)604 {605 int rc2;606 RTSemFastMutexRelease(MutexSem);607 rc2 = RTSemFastMutexDestroy(MutexSem);608 AssertRC(rc2);609 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))610 rc = rc2;611 }612 613 RTMemFree(pLogger);614 615 return rc;616 }617 618 619 /**620 * Create a logger instance clone for RC usage.621 *622 * @returns iprt status code.623 *624 * @param pLogger The logger instance to be cloned.625 * @param pLoggerGC Where to create the GC logger instance.626 * @param cbLoggerGC Amount of memory allocated to for the GC logger instance clone.627 * @param pfnLoggerGCPtr Pointer to logger wrapper function for this instance (GC Ptr).628 * @param pfnFlushGCPtr Pointer to flush function (GC Ptr).629 * @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.630 */631 RTDECL(int) RTLogCloneRC(PRTLOGGER pLogger, PRTLOGGERRC pLoggerGC, size_t cbLoggerGC,632 RTRCPTR pfnLoggerGCPtr, RTRCPTR pfnFlushGCPtr, RTUINT fFlags)633 {634 /*635 * Validate input.636 */637 if ( !pLoggerGC638 || !pfnFlushGCPtr639 || !pfnLoggerGCPtr)640 {641 AssertMsgFailed(("Invalid parameters!\n"));642 return VERR_INVALID_PARAMETER;643 }644 if (cbLoggerGC < sizeof(*pLoggerGC))645 {646 AssertMsgFailed(("%d min=%d\n", cbLoggerGC, sizeof(*pLoggerGC)));647 return VERR_INVALID_PARAMETER;648 }649 650 /*651 * Initialize GC instance.652 */653 pLoggerGC->offScratch = 0;654 pLoggerGC->fPendingPrefix = false;655 pLoggerGC->pfnLogger = pfnLoggerGCPtr;656 pLoggerGC->pfnFlush = pfnFlushGCPtr;657 pLoggerGC->u32Magic = RTLOGGERRC_MAGIC;658 pLoggerGC->fFlags = fFlags | RTLOGFLAGS_DISABLED;659 pLoggerGC->cGroups = 1;660 pLoggerGC->afGroups[0] = 0;661 662 /*663 * Resolve defaults.664 */665 if (!pLogger)666 {667 pLogger = RTLogDefaultInstance();668 if (!pLogger)669 return VINF_SUCCESS;670 }671 672 /*673 * Check if there's enough space for the groups.674 */675 if (cbLoggerGC < (size_t)RT_OFFSETOF(RTLOGGERRC, afGroups[pLogger->cGroups]))676 {677 AssertMsgFailed(("%d req=%d cGroups=%d\n", cbLoggerGC, RT_OFFSETOF(RTLOGGERRC, afGroups[pLogger->cGroups]), pLogger->cGroups));678 return VERR_INVALID_PARAMETER;679 }680 memcpy(&pLoggerGC->afGroups[0], &pLogger->afGroups[0], pLogger->cGroups * sizeof(pLoggerGC->afGroups[0]));681 pLoggerGC->cGroups = pLogger->cGroups;682 683 /*684 * Copy bits from the HC instance.685 */686 pLoggerGC->fPendingPrefix = pLogger->fPendingPrefix;687 pLoggerGC->fFlags |= pLogger->fFlags;688 689 /*690 * Check if we can remove the disabled flag.691 */692 if ( pLogger->fDestFlags693 && !((pLogger->fFlags | fFlags) & RTLOGFLAGS_DISABLED))694 pLoggerGC->fFlags &= ~RTLOGFLAGS_DISABLED;695 696 return VINF_SUCCESS;697 }698 699 700 /**701 * Flushes a GC logger instance to a HC logger.702 *703 *704 * @returns iprt status code.705 * @param pLogger The HC logger instance to flush pLoggerGC to.706 * If NULL the default logger is used.707 * @param pLoggerGC The GC logger instance to flush.708 */709 RTDECL(void) RTLogFlushGC(PRTLOGGER pLogger, PRTLOGGERRC pLoggerGC)710 {711 /*712 * Resolve defaults.713 */714 if (!pLogger)715 {716 pLogger = RTLogDefaultInstance();717 if (!pLogger)718 {719 pLoggerGC->offScratch = 0;720 return;721 }722 }723 724 /*725 * Any thing to flush?726 */727 if ( pLogger->offScratch728 || pLoggerGC->offScratch)729 {730 /*731 * Acquire logger instance sem.732 */733 int rc = rtlogLock(pLogger);734 if (RT_FAILURE(rc))735 return;736 737 /*738 * Write whatever the GC instance contains to the HC one, and then739 * flush the HC instance.740 */741 if (pLoggerGC->offScratch)742 {743 rtLogOutput(pLogger, pLoggerGC->achScratch, pLoggerGC->offScratch);744 rtLogOutput(pLogger, NULL, 0);745 pLoggerGC->offScratch = 0;746 }747 748 /*749 * Release the semaphore.750 */751 rtlogUnlock(pLogger);752 }753 }754 755 756 #ifdef IN_RING3757 /**758 * Create a logger instance for singled threaded ring-0 usage.759 *760 * @returns iprt status code.761 *762 * @param pLogger Where to create the logger instance.763 * @param cbLogger The amount of memory available for the logger instance.764 * @param pfnLogger Pointer to logger wrapper function for the clone.765 * @param pfnFlush Pointer to flush function for the clone.766 * @param fFlags Logger instance flags for the clone, a combination of the RTLOGFLAGS_* values.767 * @param fDestFlags The destination flags.768 */769 RTDECL(int) RTLogCreateForR0(PRTLOGGER pLogger, size_t cbLogger, PFNRTLOGGER pfnLogger, PFNRTLOGFLUSH pfnFlush, RTUINT fFlags, RTUINT fDestFlags)770 {771 /*772 * Validate input.773 */774 AssertPtrReturn(pLogger, VERR_INVALID_PARAMETER);775 AssertReturn(cbLogger >= sizeof(*pLogger), VERR_INVALID_PARAMETER);776 AssertReturn(pfnLogger, VERR_INVALID_PARAMETER);777 AssertReturn(pfnFlush, VERR_INVALID_PARAMETER);778 779 /*780 * Initialize the ring-0 instance.781 */782 pLogger->offScratch = 0;783 pLogger->fPendingPrefix = false;784 pLogger->pfnLogger = pfnLogger;785 pLogger->pfnFlush = pfnFlush;786 pLogger->MutexSem = NIL_RTSEMFASTMUTEX; /* Not serialized. */787 pLogger->u32Magic = RTLOGGER_MAGIC;788 pLogger->fFlags = fFlags;789 pLogger->fDestFlags = fDestFlags & ~RTLOGDEST_FILE;790 pLogger->File = NIL_RTFILE;791 pLogger->pszFilename = NULL;792 pLogger->papszGroups = NULL;793 pLogger->cMaxGroups = (cbLogger - RT_OFFSETOF(RTLOGGER, afGroups[0])) / sizeof(pLogger->afGroups[0]);794 pLogger->cGroups = 1;795 pLogger->afGroups[0] = 0;796 return VINF_SUCCESS;797 }798 #endif /* IN_RING3 */799 800 801 /**802 * Copies the group settings and flags from logger instance to another.803 *804 * @returns IPRT status code.805 * @param pDstLogger The destination logger instance.806 * @param pSrcLogger The source logger instance. If NULL the default one is used.807 * @param fFlagsOr OR mask for the flags.808 * @param fFlagsAnd AND mask for the flags.809 */810 RTDECL(int) RTLogCopyGroupsAndFlags(PRTLOGGER pDstLogger, PCRTLOGGER pSrcLogger, unsigned fFlagsOr, unsigned fFlagsAnd)811 {812 int rc;813 unsigned cGroups;814 815 /*816 * Validate input.817 */818 AssertPtrReturn(pDstLogger, VERR_INVALID_PARAMETER);819 AssertPtrNullReturn(pSrcLogger, VERR_INVALID_PARAMETER);820 821 /*822 * Resolve defaults.823 */824 if (!pSrcLogger)825 {826 pSrcLogger = RTLogDefaultInstance();827 if (!pSrcLogger)828 {829 pDstLogger->fFlags |= RTLOGFLAGS_DISABLED;830 pDstLogger->cGroups = 1;831 pDstLogger->afGroups[0] = 0;832 return VINF_SUCCESS;833 }834 }835 836 /*837 * Copy flags and group settings.838 */839 pDstLogger->fFlags = (pSrcLogger->fFlags & fFlagsAnd) | fFlagsOr;840 841 rc = VINF_SUCCESS;842 cGroups = pSrcLogger->cGroups;843 if (cGroups < pDstLogger->cMaxGroups)844 {845 AssertMsgFailed(("cMaxGroups=%zd cGroups=%zd (min size %d)\n", pDstLogger->cMaxGroups,846 pSrcLogger->cGroups, RT_OFFSETOF(RTLOGGER, afGroups[pSrcLogger->cGroups])));847 rc = VERR_INVALID_PARAMETER;848 cGroups = pDstLogger->cMaxGroups;849 }850 memcpy(&pDstLogger->afGroups[0], &pSrcLogger->afGroups[0], cGroups * sizeof(pDstLogger->afGroups[0]));851 pDstLogger->cGroups = cGroups;852 853 return rc;854 }855 856 857 /**858 * Flushes the buffer in one logger instance onto another logger.859 *860 * @returns iprt status code.861 *862 * @param pSrcLogger The logger instance to flush.863 * @param pDstLogger The logger instance to flush onto.864 * If NULL the default logger will be used.865 */866 RTDECL(void) RTLogFlushToLogger(PRTLOGGER pSrcLogger, PRTLOGGER pDstLogger)867 {868 /*869 * Resolve defaults.870 */871 if (!pDstLogger)872 {873 pDstLogger = RTLogDefaultInstance();874 if (!pDstLogger)875 {876 /* flushing to "/dev/null". */877 if (pSrcLogger->offScratch)878 {879 int rc = rtlogLock(pSrcLogger);880 if (RT_SUCCESS(rc))881 {882 pSrcLogger->offScratch = 0;883 rtlogLock(pSrcLogger);884 }885 }886 return;887 }888 }889 890 /*891 * Any thing to flush?892 */893 if ( pSrcLogger->offScratch894 || pDstLogger->offScratch)895 {896 /*897 * Acquire logger semaphores.898 */899 int rc = rtlogLock(pDstLogger);900 if (RT_FAILURE(rc))901 return;902 rc = rtlogLock(pSrcLogger);903 if (RT_SUCCESS(rc))904 {905 /*906 * Write whatever the GC instance contains to the HC one, and then907 * flush the HC instance.908 */909 if (pSrcLogger->offScratch)910 {911 rtLogOutput(pDstLogger, pSrcLogger->achScratch, pSrcLogger->offScratch);912 rtLogOutput(pDstLogger, NULL, 0);913 pSrcLogger->offScratch = 0;914 }915 916 /*917 * Release the semaphores.918 */919 rtlogUnlock(pSrcLogger);920 }921 rtlogUnlock(pDstLogger);922 }923 }924 925 926 /**927 * Matches a group name with a pattern mask in an case insensitive manner (ASCII).928 *929 * @returns true if matching and *ppachMask set to the end of the pattern.930 * @returns false if no match.931 * @param pszGrp The group name.932 * @param ppachMask Pointer to the pointer to the mask. Only wildcard supported is '*'.933 * @param cchMask The length of the mask, including modifiers. The modifiers is why934 * we update *ppachMask on match.935 */936 static bool rtlogIsGroupMatching(const char *pszGrp, const char **ppachMask, unsigned cchMask)937 {938 const char *pachMask;939 940 if (!pszGrp || !*pszGrp)941 return false;942 pachMask = *ppachMask;943 for (;;)944 {945 if (RT_C_TO_LOWER(*pszGrp) != RT_C_TO_LOWER(*pachMask))946 {947 const char *pszTmp;948 949 /*950 * Check for wildcard and do a minimal match if found.951 */952 if (*pachMask != '*')953 return false;954 955 /* eat '*'s. */956 do pachMask++;957 while (--cchMask && *pachMask == '*');958 959 /* is there more to match? */960 if ( !cchMask961 || *pachMask == '.'962 || *pachMask == '=')963 break; /* we're good */964 965 /* do extremely minimal matching (fixme) */966 pszTmp = strchr(pszGrp, RT_C_TO_LOWER(*pachMask));967 if (!pszTmp)968 pszTmp = strchr(pszGrp, RT_C_TO_UPPER(*pachMask));969 if (!pszTmp)970 return false;971 pszGrp = pszTmp;972 continue;973 }974 975 /* done? */976 if (!*++pszGrp)977 {978 /* trailing wildcard is ok. */979 do980 {981 pachMask++;982 cchMask--;983 } while (cchMask && *pachMask == '*');984 if ( !cchMask985 || *pachMask == '.'986 || *pachMask == '=')987 break; /* we're good */988 return false;989 }990 991 if (!--cchMask)992 return false;993 pachMask++;994 }995 996 /* match */997 *ppachMask = pachMask;998 return true;999 }1000 1001 1002 /**1003 * Updates the group settings for the logger instance using the specified1004 * specification string.1005 *1006 * @returns iprt status code.1007 * Failures can safely be ignored.1008 * @param pLogger Logger instance.1009 * @param pszVar Value to parse.1010 */1011 RTDECL(int) RTLogGroupSettings(PRTLOGGER pLogger, const char *pszVar)1012 {1013 /*1014 * Resolve defaults.1015 */1016 if (!pLogger)1017 {1018 pLogger = RTLogDefaultInstance();1019 if (!pLogger)1020 return VINF_SUCCESS;1021 }1022 1023 /*1024 * Iterate the string.1025 */1026 while (*pszVar)1027 {1028 /*1029 * Skip prefixes (blanks, ;, + and -).1030 */1031 bool fEnabled = true;1032 char ch;1033 const char *pszStart;1034 unsigned i;1035 size_t cch;1036 1037 while ((ch = *pszVar) == '+' || ch == '-' || ch == ' ' || ch == '\t' || ch == '\n' || ch == ';')1038 {1039 if (ch == '+' || ch == '-' || ';')1040 fEnabled = ch != '-';1041 pszVar++;1042 }1043 if (!*pszVar)1044 break;1045 1046 /*1047 * Find end.1048 */1049 pszStart = pszVar;1050 while ((ch = *pszVar) != '\0' && ch != '+' && ch != '-' && ch != ' ' && ch != '\t')1051 pszVar++;1052 1053 /*1054 * Find the group (ascii case insensitive search).1055 * Special group 'all'.1056 */1057 cch = pszVar - pszStart;1058 if ( cch >= 31059 && (pszStart[0] == 'a' || pszStart[0] == 'A')1060 && (pszStart[1] == 'l' || pszStart[1] == 'L')1061 && (pszStart[2] == 'l' || pszStart[2] == 'L')1062 && (cch == 3 || pszStart[3] == '.' || pszStart[3] == '='))1063 {1064 /*1065 * All.1066 */1067 unsigned fFlags = cch == 31068 ? RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_11069 : rtlogGroupFlags(&pszStart[3]);1070 for (i = 0; i < pLogger->cGroups; i++)1071 {1072 if (fEnabled)1073 pLogger->afGroups[i] |= fFlags;1074 else1075 pLogger->afGroups[i] &= ~fFlags;1076 }1077 }1078 else1079 {1080 /*1081 * Specific group(s).1082 */1083 for (i = 0; i < pLogger->cGroups; i++)1084 {1085 const char *psz2 = (const char*)pszStart;1086 if (rtlogIsGroupMatching(pLogger->papszGroups[i], &psz2, cch))1087 {1088 unsigned fFlags = RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1;1089 if (*psz2 == '.' || *psz2 == '=')1090 fFlags = rtlogGroupFlags(psz2);1091 if (fEnabled)1092 pLogger->afGroups[i] |= fFlags;1093 else1094 pLogger->afGroups[i] &= ~fFlags;1095 }1096 } /* for each group */1097 }1098 1099 } /* parse specification */1100 1101 return VINF_SUCCESS;1102 }1103 1104 1105 /**1106 * Interprets the group flags suffix.1107 *1108 * @returns Flags specified. (0 is possible!)1109 * @param psz Start of Suffix. (Either dot or equal sign.)1110 */1111 static unsigned rtlogGroupFlags(const char *psz)1112 {1113 unsigned fFlags = 0;1114 1115 /*1116 * Litteral flags.1117 */1118 while (*psz == '.')1119 {1120 static struct1121 {1122 const char *pszFlag; /* lowercase!! */1123 unsigned fFlag;1124 } aFlags[] =1125 {1126 { "eo", RTLOGGRPFLAGS_ENABLED },1127 { "enabledonly",RTLOGGRPFLAGS_ENABLED },1128 { "e", RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1 },1129 { "enabled", RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1 },1130 { "l1", RTLOGGRPFLAGS_LEVEL_1 },1131 { "level1", RTLOGGRPFLAGS_LEVEL_1 },1132 { "l", RTLOGGRPFLAGS_LEVEL_2 },1133 { "l2", RTLOGGRPFLAGS_LEVEL_2 },1134 { "level2", RTLOGGRPFLAGS_LEVEL_2 },1135 { "l3", RTLOGGRPFLAGS_LEVEL_3 },1136 { "level3", RTLOGGRPFLAGS_LEVEL_3 },1137 { "l4", RTLOGGRPFLAGS_LEVEL_4 },1138 { "level4", RTLOGGRPFLAGS_LEVEL_4 },1139 { "l5", RTLOGGRPFLAGS_LEVEL_5 },1140 { "level5", RTLOGGRPFLAGS_LEVEL_5 },1141 { "l6", RTLOGGRPFLAGS_LEVEL_6 },1142 { "level6", RTLOGGRPFLAGS_LEVEL_6 },1143 { "f", RTLOGGRPFLAGS_FLOW },1144 { "flow", RTLOGGRPFLAGS_FLOW },1145 1146 { "lelik", RTLOGGRPFLAGS_LELIK },1147 { "michael", RTLOGGRPFLAGS_MICHAEL },1148 { "dmik", RTLOGGRPFLAGS_DMIK },1149 { "sunlover", RTLOGGRPFLAGS_SUNLOVER },1150 { "achim", RTLOGGRPFLAGS_ACHIM },1151 { "achimha", RTLOGGRPFLAGS_ACHIM },1152 { "s", RTLOGGRPFLAGS_SANDER },1153 { "sander", RTLOGGRPFLAGS_SANDER },1154 { "sandervl", RTLOGGRPFLAGS_SANDER },1155 { "klaus", RTLOGGRPFLAGS_KLAUS },1156 { "frank", RTLOGGRPFLAGS_FRANK },1157 { "b", RTLOGGRPFLAGS_BIRD },1158 { "bird", RTLOGGRPFLAGS_BIRD },1159 { "aleksey", RTLOGGRPFLAGS_ALEKSEY },1160 { "n", RTLOGGRPFLAGS_NONAME },1161 { "noname", RTLOGGRPFLAGS_NONAME }1162 };1163 unsigned i;1164 bool fFound = false;1165 psz++;1166 for (i = 0; i < ELEMENTS(aFlags) && !fFound; i++)1167 {1168 const char *psz1 = aFlags[i].pszFlag;1169 const char *psz2 = psz;1170 while (*psz1 == RT_C_TO_LOWER(*psz2))1171 {1172 psz1++;1173 psz2++;1174 if (!*psz1)1175 {1176 if ( (*psz2 >= 'a' && *psz2 <= 'z')1177 || (*psz2 >= 'A' && *psz2 <= 'Z')1178 || (*psz2 >= '0' && *psz2 <= '9') )1179 break;1180 fFlags |= aFlags[i].fFlag;1181 fFound = true;1182 psz = psz2;1183 break;1184 }1185 } /* strincmp */1186 } /* for each flags */1187 }1188 1189 /*1190 * Flag value.1191 */1192 if (*psz == '=')1193 {1194 psz++;1195 if (*psz == '~')1196 fFlags = ~RTStrToInt32(psz + 1);1197 else1198 fFlags = RTStrToInt32(psz);1199 }1200 1201 return fFlags;1202 }1203 1204 #endif /* !IN_GC */1205 1206 1207 /**1208 * Updates the flags for the logger instance using the specified1209 * specification string.1210 *1211 * @returns iprt status code.1212 * Failures can safely be ignored.1213 * @param pLogger Logger instance (NULL for default logger).1214 * @param pszVar Value to parse.1215 */1216 RTDECL(int) RTLogFlags(PRTLOGGER pLogger, const char *pszVar)1217 {1218 int rc = VINF_SUCCESS;1219 1220 /*1221 * Resolve defaults.1222 */1223 if (!pLogger)1224 {1225 pLogger = RTLogDefaultInstance();1226 if (!pLogger)1227 return VINF_SUCCESS;1228 }1229 1230 /*1231 * Iterate the string.1232 */1233 while (*pszVar)1234 {1235 /* parse instruction. */1236 static struct1237 {1238 const char *pszInstr;1239 size_t cchInstr;1240 unsigned fFlag;1241 bool fInverted;1242 } const aDest[] =1243 {1244 { "disabled", sizeof("disabled" ) - 1, RTLOGFLAGS_DISABLED, false },1245 { "enabled", sizeof("enabled" ) - 1, RTLOGFLAGS_DISABLED, true },1246 { "buffered", sizeof("buffered" ) - 1, RTLOGFLAGS_BUFFERED, false },1247 { "unbuffered", sizeof("unbuffered" ) - 1, RTLOGFLAGS_BUFFERED, true },1248 { "usecrlf", sizeof("usecrlf" ) - 1, RTLOGFLAGS_USECRLF, true },1249 { "uself", sizeof("uself" ) - 1, RTLOGFLAGS_USECRLF, false },1250 { "append", sizeof("append" ) - 1, RTLOGFLAGS_APPEND, false },1251 { "overwrite", sizeof("overwrite" ) - 1, RTLOGFLAGS_APPEND, true },1252 { "rel", sizeof("rel" ) - 1, RTLOGFLAGS_REL_TS, false },1253 { "abs", sizeof("abs" ) - 1, RTLOGFLAGS_REL_TS, true },1254 { "dec", sizeof("dec" ) - 1, RTLOGFLAGS_DECIMAL_TS, false },1255 { "hex", sizeof("hex" ) - 1, RTLOGFLAGS_DECIMAL_TS, true },1256 { "lockcnts", sizeof("lockcnts" ) - 1, RTLOGFLAGS_PREFIX_LOCK_COUNTS, false },1257 { "cpuid", sizeof("cpuid" ) - 1, RTLOGFLAGS_PREFIX_CPUID, false },1258 { "pid", sizeof("pid" ) - 1, RTLOGFLAGS_PREFIX_PID, false },1259 { "flagno", sizeof("flagno" ) - 1, RTLOGFLAGS_PREFIX_FLAG_NO, false },1260 { "flag", sizeof("flag" ) - 1, RTLOGFLAGS_PREFIX_FLAG, false },1261 { "groupno", sizeof("groupno" ) - 1, RTLOGFLAGS_PREFIX_GROUP_NO, false },1262 { "group", sizeof("group" ) - 1, RTLOGFLAGS_PREFIX_GROUP, false },1263 { "tid", sizeof("tid" ) - 1, RTLOGFLAGS_PREFIX_TID, false },1264 { "thread", sizeof("thread" ) - 1, RTLOGFLAGS_PREFIX_THREAD, false },1265 { "timeprog", sizeof("timeprog" ) - 1, RTLOGFLAGS_PREFIX_TIME_PROG, false },1266 { "time", sizeof("time" ) - 1, RTLOGFLAGS_PREFIX_TIME, false },1267 { "msprog", sizeof("msprog" ) - 1, RTLOGFLAGS_PREFIX_MS_PROG, false },1268 { "tsc", sizeof("tsc" ) - 1, RTLOGFLAGS_PREFIX_TSC, false }, /* before ts! */1269 { "ts", sizeof("ts" ) - 1, RTLOGFLAGS_PREFIX_TS, false },1270 };1271 1272 /* check no prefix. */1273 bool fNo = false;1274 char ch;1275 unsigned i;1276 1277 /* skip blanks. */1278 while (RT_C_IS_SPACE(*pszVar))1279 pszVar++;1280 if (!*pszVar)1281 return rc;1282 1283 while ((ch = *pszVar) != '\0')1284 {1285 if (ch == 'n' && pszVar[1] == 'o')1286 {1287 pszVar += 2;1288 fNo = !fNo;1289 }1290 else if (ch == '+')1291 {1292 pszVar++;1293 fNo = true;1294 }1295 else if (ch == '-' || ch == '!' || ch == '~')1296 {1297 pszVar++;1298 fNo = !fNo;1299 }1300 else1301 break;1302 }1303 1304 /* instruction. */1305 for (i = 0; i < ELEMENTS(aDest); i++)1306 {1307 if (!strncmp(pszVar, aDest[i].pszInstr, aDest[i].cchInstr))1308 {1309 if (fNo == aDest[i].fInverted)1310 pLogger->fFlags |= aDest[i].fFlag;1311 else1312 pLogger->fFlags &= ~aDest[i].fFlag;1313 pszVar += aDest[i].cchInstr;1314 break;1315 }1316 }1317 1318 /* unknown instruction? */1319 if (i >= ELEMENTS(aDest))1320 {1321 AssertMsgFailed(("Invalid flags! unknown instruction %.20s\n", pszVar));1322 pszVar++;1323 }1324 1325 /* skip blanks and delimiters. */1326 while (RT_C_IS_SPACE(*pszVar) || *pszVar == ';')1327 pszVar++;1328 } /* while more environment variable value left */1329 1330 return rc;1331 }1332 1333 1334 /**1335 * Flushes the specified logger.1336 *1337 * @param pLogger The logger instance to flush.1338 * If NULL the default instance is used. The default instance1339 * will not be initialized by this call.1340 */1341 RTDECL(void) RTLogFlush(PRTLOGGER pLogger)1342 {1343 /*1344 * Resolve defaults.1345 */1346 if (!pLogger)1347 {1348 #ifdef IN_GC1349 pLogger = &g_Logger;1350 #else1351 pLogger = g_pLogger;1352 #endif1353 if (!pLogger)1354 return;1355 }1356 1357 /*1358 * Any thing to flush?1359 */1360 if (pLogger->offScratch)1361 {1362 #ifndef IN_GC1363 /*1364 * Acquire logger instance sem.1365 */1366 int rc = rtlogLock(pLogger);1367 if (RT_FAILURE(rc))1368 return;1369 #endif1370 /*1371 * Call worker.1372 */1373 rtlogFlush(pLogger);1374 1375 #ifndef IN_GC1376 /*1377 * Release the semaphore.1378 */1379 rtlogUnlock(pLogger);1380 #endif1381 }1382 }1383 1384 1385 /**1386 * Gets the default logger instance.1387 *1388 * @returns Pointer to default logger instance.1389 * @returns NULL if no default logger instance available.1390 */1391 RTDECL(PRTLOGGER) RTLogDefaultInstance(void)1392 {1393 #ifdef IN_GC1394 return &g_Logger;1395 1396 #else /* !IN_GC */1397 # ifdef IN_RING01398 /*1399 * Check per thread loggers first.1400 */1401 if (g_cPerThreadLoggers)1402 {1403 const RTNATIVETHREAD Self = RTThreadNativeSelf();1404 int32_t i = ELEMENTS(g_aPerThreadLoggers);1405 while (i-- > 0)1406 if (g_aPerThreadLoggers[i].NativeThread == Self)1407 return g_aPerThreadLoggers[i].pLogger;1408 }1409 # endif /* IN_RING0 */1410 1411 /*1412 * If no per thread logger, use the default one.1413 */1414 if (!g_pLogger)1415 g_pLogger = RTLogDefaultInit();1416 return g_pLogger;1417 #endif /* !IN_GC */1418 }1419 1420 1421 #ifndef IN_GC1422 /**1423 * Sets the default logger instance.1424 *1425 * @returns iprt status code.1426 * @param pLogger The new default logger instance.1427 */1428 RTDECL(PRTLOGGER) RTLogSetDefaultInstance(PRTLOGGER pLogger)1429 {1430 return (PRTLOGGER)ASMAtomicXchgPtr((void * volatile *)&g_pLogger, pLogger);1431 }1432 #endif /* !IN_GC */1433 1434 1435 #ifdef IN_RING01436 /**1437 * Changes the default logger instance for the current thread.1438 *1439 * @returns IPRT status code.1440 * @param pLogger The logger instance. Pass NULL for deregistration.1441 * @param uKey Associated key for cleanup purposes. If pLogger is NULL,1442 * all instances with this key will be deregistered. So in1443 * order to only deregister the instance associated with the1444 * current thread use 0.1445 */1446 RTDECL(int) RTLogSetDefaultInstanceThread(PRTLOGGER pLogger, uintptr_t uKey)1447 {1448 int rc;1449 RTNATIVETHREAD Self = RTThreadNativeSelf();1450 if (pLogger)1451 {1452 int32_t i;1453 unsigned j;1454 1455 AssertReturn(pLogger->u32Magic == RTLOGGER_MAGIC, VERR_INVALID_MAGIC);1456 1457 /*1458 * Iterate the table to see if there is already an entry for this thread.1459 */1460 i = ELEMENTS(g_aPerThreadLoggers);1461 while (i-- > 0)1462 if (g_aPerThreadLoggers[i].NativeThread == Self)1463 {1464 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, (void *)uKey);1465 g_aPerThreadLoggers[i].pLogger = pLogger;1466 return VINF_SUCCESS;1467 }1468 1469 /*1470 * Allocate a new table entry.1471 */1472 i = ASMAtomicIncS32(&g_cPerThreadLoggers);1473 if (i > (int32_t)ELEMENTS(g_aPerThreadLoggers))1474 {1475 ASMAtomicDecS32(&g_cPerThreadLoggers);1476 return VERR_BUFFER_OVERFLOW; /* horrible error code! */1477 }1478 1479 for (j = 0; j < 10; j++)1480 {1481 i = ELEMENTS(g_aPerThreadLoggers);1482 while (i-- > 0)1483 {1484 AssertCompile(sizeof(RTNATIVETHREAD) == sizeof(void*));1485 if ( g_aPerThreadLoggers[i].NativeThread == NIL_RTNATIVETHREAD1486 && ASMAtomicCmpXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].NativeThread, (void *)Self, (void *)NIL_RTNATIVETHREAD))1487 {1488 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, (void *)uKey);1489 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].pLogger, pLogger);1490 return VINF_SUCCESS;1491 }1492 }1493 }1494 1495 ASMAtomicDecS32(&g_cPerThreadLoggers);1496 rc = VERR_INTERNAL_ERROR;1497 }1498 else1499 {1500 /*1501 * Search the array for the current thread.1502 */1503 int32_t i = ELEMENTS(g_aPerThreadLoggers);1504 while (i-- > 0)1505 if ( g_aPerThreadLoggers[i].NativeThread == Self1506 || g_aPerThreadLoggers[i].uKey == uKey)1507 {1508 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, NULL);1509 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].pLogger, NULL);1510 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].NativeThread, (void *)NIL_RTNATIVETHREAD);1511 ASMAtomicDecS32(&g_cPerThreadLoggers);1512 }1513 1514 rc = VINF_SUCCESS;1515 }1516 return rc;1517 }1518 #endif1519 72 1520 73 … … 1550 103 1551 104 /** 1552 * Write to a logger instance.1553 *1554 * @param pLogger Pointer to logger instance.1555 * @param pvCallerRet Ignored.1556 * @param pszFormat Format string.1557 * @param ... Format arguments.1558 */1559 RTDECL(void) RTLogLogger(PRTLOGGER pLogger, void *pvCallerRet, const char *pszFormat, ...)1560 {1561 va_list args;1562 va_start(args, pszFormat);1563 #if defined(RT_OS_DARWIN) && defined(RT_ARCH_X86) && defined(IN_RING3)1564 /* manually align the stack before doing the call.1565 * We boldly assume that there is a stack frame here! */1566 __asm__ __volatile__("andl $-32, %%esp\t\n" ::: "%esp");1567 RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);1568 #else1569 RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);1570 #endif1571 va_end(args);1572 }1573 1574 1575 /**1576 * Write to a logger instance.1577 *1578 * @param pLogger Pointer to logger instance.1579 * @param pszFormat Format string.1580 * @param args Format arguments.1581 */1582 RTDECL(void) RTLogLoggerV(PRTLOGGER pLogger, const char *pszFormat, va_list args)1583 {1584 RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);1585 }1586 1587 1588 /**1589 * Write to a logger instance.1590 *1591 * This function will check whether the instance, group and flags makes up a1592 * logging kind which is currently enabled before writing anything to the log.1593 *1594 * @param pLogger Pointer to logger instance. If NULL the default logger instance will be attempted.1595 * @param fFlags The logging flags.1596 * @param iGroup The group.1597 * The value ~0U is reserved for compatability with RTLogLogger[V] and is1598 * only for internal usage!1599 * @param pszFormat Format string.1600 * @param ... Format arguments.1601 * @remark This is a worker function of LogIt.1602 */1603 RTDECL(void) RTLogLoggerEx(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, ...)1604 {1605 va_list args;1606 va_start(args, pszFormat);1607 RTLogLoggerExV(pLogger, fFlags, iGroup, pszFormat, args);1608 va_end(args);1609 }1610 1611 1612 /**1613 * Write to a logger instance.1614 *1615 * This function will check whether the instance, group and flags makes up a1616 * logging kind which is currently enabled before writing anything to the log.1617 *1618 * @param pLogger Pointer to logger instance. If NULL the default logger instance will be attempted.1619 * @param fFlags The logging flags.1620 * @param iGroup The group.1621 * The value ~0U is reserved for compatability with RTLogLogger[V] and is1622 * only for internal usage!1623 * @param pszFormat Format string.1624 * @param args Format arguments.1625 */1626 RTDECL(void) RTLogLoggerExV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)1627 {1628 /*1629 * A NULL logger means default instance.1630 */1631 if (!pLogger)1632 {1633 pLogger = RTLogDefaultInstance();1634 if (!pLogger)1635 return;1636 }1637 rtlogLogger(pLogger, fFlags, iGroup, pszFormat, args);1638 }1639 1640 1641 /**1642 * Write to a logger instance, defaulting to the release one.1643 *1644 * This function will check whether the instance, group and flags makes up a1645 * logging kind which is currently enabled before writing anything to the log.1646 *1647 * @param pLogger Pointer to logger instance.1648 * @param fFlags The logging flags.1649 * @param iGroup The group.1650 * The value ~0U is reserved for compatability with RTLogLogger[V] and is1651 * only for internal usage!1652 * @param pszFormat Format string.1653 * @param ... Format arguments.1654 * @remark This is a worker function for LogRelIt.1655 */1656 RTDECL(void) RTLogRelLogger(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, ...)1657 {1658 va_list args;1659 va_start(args, pszFormat);1660 RTLogRelLoggerV(pLogger, fFlags, iGroup, pszFormat, args);1661 va_end(args);1662 }1663 1664 1665 /**1666 105 * Write to a logger instance, defaulting to the release one. 1667 106 * … … 1688 127 return; 1689 128 } 1690 rtlogLogger(pLogger, fFlags, iGroup, pszFormat, args); 1691 } 1692 1693 1694 /** 1695 * Worker for the RTLog[Rel]Logger*() functions. 1696 * 1697 * @param pLogger Pointer to logger instance. 1698 * @param fFlags The logging flags. 1699 * @param iGroup The group. 1700 * The value ~0U is reserved for compatability with RTLogLogger[V] and is 1701 * only for internal usage! 1702 * @param pszFormat Format string. 1703 * @param args Format arguments. 1704 */ 1705 static void rtlogLogger(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args) 1706 { 1707 int rc; 1708 1709 /* 1710 * Validate and correct iGroup. 1711 */ 1712 if (iGroup != ~0U && iGroup >= pLogger->cGroups) 1713 iGroup = 0; 1714 1715 /* 1716 * If no output, then just skip it. 1717 */ 1718 if ( (pLogger->fFlags & RTLOGFLAGS_DISABLED) 1719 #ifndef IN_GC 1720 || !pLogger->fDestFlags 1721 #endif 1722 || !pszFormat || !*pszFormat) 1723 return; 1724 if ( iGroup != ~0U 1725 && (pLogger->afGroups[iGroup] & (fFlags | RTLOGGRPFLAGS_ENABLED)) != (fFlags | RTLOGGRPFLAGS_ENABLED)) 1726 return; 1727 1728 /* 1729 * Acquire logger instance sem. 1730 */ 1731 rc = rtlogLock(pLogger); 1732 if (RT_FAILURE(rc)) 1733 return; 1734 1735 /* 1736 * Format the message and perhaps flush it. 1737 */ 1738 if (pLogger->fFlags & (RTLOGFLAGS_PREFIX_MASK | RTLOGFLAGS_USECRLF)) 1739 { 1740 RTLOGOUTPUTPREFIXEDARGS OutputArgs; 1741 OutputArgs.pLogger = pLogger; 1742 OutputArgs.iGroup = iGroup; 1743 OutputArgs.fFlags = fFlags; 1744 RTLogFormatV(rtLogOutputPrefixed, &OutputArgs, pszFormat, args); 1745 } 1746 else 1747 RTLogFormatV(rtLogOutput, pLogger, pszFormat, args); 1748 if ( !(pLogger->fFlags & RTLOGFLAGS_BUFFERED) 1749 && pLogger->offScratch) 1750 rtlogFlush(pLogger); 1751 1752 /* 1753 * Release the semaphore. 1754 */ 1755 rtlogUnlock(pLogger); 1756 } 1757 1758 1759 /** 1760 * printf like function for writing to the default log. 1761 * 1762 * @param pszFormat Printf like format string. 1763 * @param ... Optional arguments as specified in pszFormat. 1764 * 1765 * @remark The API doesn't support formatting of floating point numbers at the moment. 1766 */ 1767 RTDECL(void) RTLogPrintf(const char *pszFormat, ...) 1768 { 1769 va_list args; 1770 va_start(args, pszFormat); 1771 RTLogPrintfV(pszFormat, args); 1772 va_end(args); 1773 } 1774 1775 1776 /** 1777 * vprintf like function for writing to the default log. 1778 * 1779 * @param pszFormat Printf like format string. 1780 * @param args Optional arguments as specified in pszFormat. 1781 * 1782 * @remark The API doesn't support formatting of floating point numbers at the moment. 1783 */ 1784 RTDECL(void) RTLogPrintfV(const char *pszFormat, va_list args) 1785 { 1786 RTLogLoggerV(NULL, pszFormat, args); 1787 } 1788 1789 1790 /** 1791 * printf like function for writing to the default release log. 1792 * 1793 * @param pszFormat Printf like format string. 1794 * @param ... Optional arguments as specified in pszFormat. 1795 * 1796 * @remark The API doesn't support formatting of floating point numbers at the moment. 1797 */ 1798 RTDECL(void) RTLogRelPrintf(const char *pszFormat, ...) 1799 { 1800 va_list args; 1801 va_start(args, pszFormat); 1802 RTLogRelPrintfV(pszFormat, args); 1803 va_end(args); 129 RTLogLoggerExV(pLogger, fFlags, iGroup, pszFormat, args); 1804 130 } 1805 131 … … 1818 144 } 1819 145 1820 1821 /**1822 * Writes the buffer to the given log device without checking for buffered1823 * data or anything.1824 * Used by the RTLogFlush() function.1825 *1826 * @param pLogger The logger instance to write to. NULL is not allowed!1827 */1828 static void rtlogFlush(PRTLOGGER pLogger)1829 {1830 #ifndef IN_GC1831 if (pLogger->fDestFlags & RTLOGDEST_USER)1832 RTLogWriteUser(pLogger->achScratch, pLogger->offScratch);1833 1834 if (pLogger->fDestFlags & RTLOGDEST_DEBUGGER)1835 RTLogWriteDebugger(pLogger->achScratch, pLogger->offScratch);1836 1837 # ifdef IN_RING31838 if (pLogger->fDestFlags & RTLOGDEST_FILE)1839 RTFileWrite(pLogger->File, pLogger->achScratch, pLogger->offScratch, NULL);1840 # endif1841 1842 if (pLogger->fDestFlags & RTLOGDEST_STDOUT)1843 RTLogWriteStdOut(pLogger->achScratch, pLogger->offScratch);1844 1845 if (pLogger->fDestFlags & RTLOGDEST_STDERR)1846 RTLogWriteStdErr(pLogger->achScratch, pLogger->offScratch);1847 1848 # if (defined(IN_RING0) || defined(IN_GC)) && !defined(LOG_NO_COM)1849 if (pLogger->fDestFlags & RTLOGDEST_COM)1850 RTLogWriteCom(pLogger->achScratch, pLogger->offScratch);1851 # endif1852 #endif /* !IN_GC */1853 1854 if (pLogger->pfnFlush)1855 pLogger->pfnFlush(pLogger);1856 1857 /* empty the buffer. */1858 pLogger->offScratch = 0;1859 }1860 1861 1862 /**1863 * Callback for RTLogFormatV which writes to the com port.1864 * See PFNLOGOUTPUT() for details.1865 */1866 static DECLCALLBACK(size_t) rtLogOutput(void *pv, const char *pachChars, size_t cbChars)1867 {1868 PRTLOGGER pLogger = (PRTLOGGER)pv;1869 if (cbChars)1870 {1871 size_t cbRet = 0;1872 for (;;)1873 {1874 #if defined(DEBUG) && defined(IN_RING3)1875 /* sanity */1876 if (pLogger->offScratch >= sizeof(pLogger->achScratch))1877 {1878 fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",1879 pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));1880 AssertBreakpoint(); AssertBreakpoint();1881 }1882 #endif1883 1884 /* how much */1885 size_t cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;1886 if (cb > cbChars)1887 cb = cbChars;1888 1889 /* copy */1890 memcpy(&pLogger->achScratch[pLogger->offScratch], pachChars, cb);1891 1892 /* advance */1893 pLogger->offScratch += cb;1894 cbRet += cb;1895 cbChars -= cb;1896 1897 /* done? */1898 if (cbChars <= 0)1899 return cbRet;1900 1901 pachChars += cb;1902 1903 /* flush */1904 rtlogFlush(pLogger);1905 }1906 1907 /* won't ever get here! */1908 }1909 else1910 {1911 /*1912 * Termination call.1913 * There's always space for a terminator, and it's not counted.1914 */1915 pLogger->achScratch[pLogger->offScratch] = '\0';1916 return 0;1917 }1918 }1919 1920 1921 1922 /**1923 * Callback for RTLogFormatV which writes to the logger instance.1924 * This version supports prefixes.1925 *1926 * See PFNLOGOUTPUT() for details.1927 */1928 static DECLCALLBACK(size_t) rtLogOutputPrefixed(void *pv, const char *pachChars, size_t cbChars)1929 {1930 PRTLOGOUTPUTPREFIXEDARGS pArgs = (PRTLOGOUTPUTPREFIXEDARGS)pv;1931 PRTLOGGER pLogger = pArgs->pLogger;1932 if (cbChars)1933 {1934 size_t cbRet = 0;1935 for (;;)1936 {1937 size_t cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;1938 char *psz;1939 const char *pszNewLine;1940 1941 /*1942 * Pending prefix?1943 */1944 if (pLogger->fPendingPrefix)1945 {1946 pLogger->fPendingPrefix = false;1947 1948 #if defined(DEBUG) && defined(IN_RING3)1949 /* sanity */1950 if (pLogger->offScratch >= sizeof(pLogger->achScratch))1951 {1952 fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",1953 pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));1954 AssertBreakpoint(); AssertBreakpoint();1955 }1956 #endif1957 1958 /*1959 * Flush the buffer if there isn't enough room for the maximum prefix config.1960 * Max is 224, add a couple of extra bytes.1961 */1962 if (cb < 224 + 16)1963 {1964 rtlogFlush(pLogger);1965 cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;1966 }1967 1968 /*1969 * Write the prefixes.1970 * psz is pointing to the current position.1971 */1972 psz = &pLogger->achScratch[pLogger->offScratch];1973 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TS)1974 {1975 #if defined(IN_RING3) || defined(IN_GC)1976 uint64_t u64 = RTTimeNanoTS();1977 #else1978 uint64_t u64 = ~0;1979 #endif1980 int iBase = 16;1981 unsigned int fFlags = RTSTR_F_ZEROPAD;1982 if (pLogger->fFlags & RTLOGFLAGS_DECIMAL_TS)1983 {1984 iBase = 10;1985 fFlags = 0;1986 }1987 if (pLogger->fFlags & RTLOGFLAGS_REL_TS)1988 {1989 static volatile uint64_t s_u64LastTs;1990 uint64_t u64DiffTs = u64 - s_u64LastTs;1991 s_u64LastTs = u64;1992 /* We could have been preempted just before reading of s_u64LastTs by1993 * another thread which wrote s_u64LastTs. In that case the difference1994 * is negative which we simply ignore. */1995 u64 = (int64_t)u64DiffTs < 0 ? 0 : u64DiffTs;1996 }1997 /* 1E15 nanoseconds = 11 days */1998 psz += RTStrFormatNumber(psz, u64, iBase, 16, 0, fFlags); /* +17 */1999 *psz++ = ' ';2000 }2001 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TSC)2002 {2003 uint64_t u64 = ASMReadTSC();2004 int iBase = 16;2005 unsigned int fFlags = RTSTR_F_ZEROPAD;2006 if (pLogger->fFlags & RTLOGFLAGS_DECIMAL_TS)2007 {2008 iBase = 10;2009 fFlags = 0;2010 }2011 if (pLogger->fFlags & RTLOGFLAGS_REL_TS)2012 {2013 static volatile uint64_t s_u64LastTsc;2014 uint64_t u64DiffTsc = u64 - s_u64LastTsc;2015 s_u64LastTsc = u64;2016 /* We could have been preempted just before reading of s_u64LastTsc by2017 * another thread which wrote s_u64LastTsc. In that case the difference2018 * is negative which we simply ignore. */2019 u64 = u64DiffTsc < 0 ? 0 : u64DiffTsc;2020 }2021 /* 1E15 ticks at 4GHz = 69 hours */2022 psz += RTStrFormatNumber(psz, u64, iBase, 16, 0, fFlags); /* +17 */2023 *psz++ = ' ';2024 }2025 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_MS_PROG)2026 {2027 #if defined(IN_RING3) || defined(IN_GC)2028 uint64_t u64 = RTTimeProgramMilliTS();2029 #else2030 uint64_t u64 = 0;2031 #endif2032 /* 1E8 milliseconds = 27 hours */2033 psz += RTStrFormatNumber(psz, u64, 10, 9, 0, RTSTR_F_ZEROPAD);2034 *psz++ = ' ';2035 }2036 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TIME)2037 {2038 #ifdef IN_RING32039 RTTIMESPEC TimeSpec;2040 RTTIME Time;2041 RTTimeExplode(&Time, RTTimeNow(&TimeSpec));2042 psz += RTStrFormatNumber(psz, Time.u8Hour, 10, 2, 0, RTSTR_F_ZEROPAD);2043 *psz++ = ':';2044 psz += RTStrFormatNumber(psz, Time.u8Minute, 10, 2, 0, RTSTR_F_ZEROPAD);2045 *psz++ = ':';2046 psz += RTStrFormatNumber(psz, Time.u8Second, 10, 2, 0, RTSTR_F_ZEROPAD);2047 *psz++ = '.';2048 psz += RTStrFormatNumber(psz, Time.u32Nanosecond / 1000000, 10, 3, 0, RTSTR_F_ZEROPAD);2049 *psz++ = ' '; /* +17 (3+1+3+1+3+1+4+1) */2050 #else2051 memset(psz, ' ', 13);2052 psz += 13;2053 #endif2054 }2055 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TIME_PROG)2056 {2057 #ifdef IN_RING32058 uint64_t u64 = RTTimeProgramMilliTS();2059 psz += RTStrFormatNumber(psz, (uint32_t)(u64 / (60 * 60 * 1000)), 10, 2, 0, RTSTR_F_ZEROPAD);2060 *psz++ = ':';2061 uint32_t u32 = (uint32_t)(u64 % (60 * 60 * 1000));2062 psz += RTStrFormatNumber(psz, u32 / (60 * 1000), 10, 2, 0, RTSTR_F_ZEROPAD);2063 *psz++ = ':';2064 u32 %= 60 * 1000;2065 psz += RTStrFormatNumber(psz, u32 / 1000, 10, 2, 0, RTSTR_F_ZEROPAD);2066 *psz++ = '.';2067 psz += RTStrFormatNumber(psz, u32 % 1000, 10, 3, 0, RTSTR_F_ZEROPAD);2068 *psz++ = ' '; /* +20 (9+1+2+1+2+1+3+1) */2069 #else2070 memset(psz, ' ', 13);2071 psz += 13;2072 #endif2073 }2074 # if 02075 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_DATETIME)2076 {2077 char szDate[32];2078 RTTIMESPEC Time;2079 RTTimeSpecToString(RTTimeNow(&Time), szDate, sizeof(szDate));2080 size_t cch = strlen(szDate);2081 memcpy(psz, szDate, cch);2082 psz += cch;2083 *psz++ = ' '; /* +32 */2084 }2085 # endif2086 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_PID)2087 {2088 #ifndef IN_GC2089 RTPROCESS Process = RTProcSelf();2090 #else2091 RTPROCESS Process = NIL_RTPROCESS;2092 #endif2093 psz += RTStrFormatNumber(psz, Process, 16, sizeof(RTPROCESS) * 2, 0, RTSTR_F_ZEROPAD);2094 *psz++ = ' '; /* +9 */2095 }2096 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TID)2097 {2098 #ifndef IN_GC2099 RTNATIVETHREAD Thread = RTThreadNativeSelf();2100 #else2101 RTNATIVETHREAD Thread = NIL_RTNATIVETHREAD;2102 #endif2103 psz += RTStrFormatNumber(psz, Thread, 16, sizeof(RTNATIVETHREAD) * 2, 0, RTSTR_F_ZEROPAD);2104 *psz++ = ' '; /* +17 */2105 }2106 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_THREAD)2107 {2108 #ifdef IN_RING32109 const char *pszName = RTThreadSelfName();2110 #elif defined IN_GC2111 const char *pszName = "EMT-GC";2112 #else2113 const char *pszName = "EMT-R0";2114 #endif2115 size_t cch = 0;2116 if (pszName)2117 {2118 cch = strlen(pszName);2119 cch = RT_MIN(cch, 16);2120 memcpy(psz, pszName, cch);2121 psz += cch;2122 }2123 do2124 *psz++ = ' ';2125 while (cch++ < 8); /* +17 */2126 }2127 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_CPUID)2128 {2129 #if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)2130 const uint8_t idCpu = ASMGetApicId();2131 #else2132 const RTCPUID idCpu = RTMpCpuId();2133 #endif2134 psz += RTStrFormatNumber(psz, idCpu, 16, sizeof(idCpu) * 2, 0, RTSTR_F_ZEROPAD);2135 *psz++ = ' '; /* +17 */2136 }2137 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_LOCK_COUNTS)2138 {2139 #ifdef IN_RING3 /** @todo implement these counters in ring-0 too? */2140 RTTHREAD Thread = RTThreadSelf();2141 if (Thread != NIL_RTTHREAD)2142 {2143 uint32_t cReadLocks = RTThreadGetReadLockCount(Thread);2144 uint32_t cWriteLocks = RTThreadGetWriteLockCount(Thread) - g_cLoggerLockCount;2145 cReadLocks = RT_MIN(0xfff, cReadLocks);2146 cWriteLocks = RT_MIN(0xfff, cWriteLocks);2147 psz += RTStrFormatNumber(psz, cReadLocks, 16, 1, 0, RTSTR_F_ZEROPAD);2148 *psz++ = '/';2149 psz += RTStrFormatNumber(psz, cWriteLocks, 16, 1, 0, RTSTR_F_ZEROPAD);2150 }2151 else2152 #endif2153 {2154 *psz++ = '?';2155 *psz++ = '/';2156 *psz++ = '?';2157 }2158 *psz++ = ' '; /* +8 */2159 }2160 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_FLAG_NO)2161 {2162 psz += RTStrFormatNumber(psz, pArgs->fFlags, 16, 8, 0, RTSTR_F_ZEROPAD);2163 *psz++ = ' '; /* +9 */2164 }2165 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_FLAG)2166 {2167 #ifdef IN_RING32168 const char *pszGroup = pArgs->iGroup != ~0U ? pLogger->papszGroups[pArgs->iGroup] : NULL;2169 #else2170 const char *pszGroup = NULL;2171 #endif2172 size_t cch = 0;2173 if (pszGroup)2174 {2175 cch = strlen(pszGroup);2176 cch = RT_MIN(cch, 16);2177 memcpy(psz, pszGroup, cch);2178 psz += cch;2179 }2180 do2181 *psz++ = ' ';2182 while (cch++ < 8); /* +17 */2183 }2184 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_GROUP_NO)2185 {2186 if (pArgs->iGroup != ~0U)2187 {2188 psz += RTStrFormatNumber(psz, pArgs->iGroup, 16, 3, 0, RTSTR_F_ZEROPAD);2189 *psz++ = ' ';2190 }2191 else2192 {2193 memcpy(psz, "-1 ", sizeof("-1 ") - 1);2194 psz += sizeof("-1 ") - 1;2195 } /* +9 */2196 }2197 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_GROUP)2198 {2199 const unsigned fGrp = pLogger->afGroups[pArgs->iGroup != ~0U ? pArgs->iGroup : 0];2200 const char *pszGroup;2201 size_t cch;2202 switch (pArgs->fFlags & fGrp)2203 {2204 case 0: pszGroup = "--------"; cch = sizeof("--------") - 1; break;2205 case RTLOGGRPFLAGS_ENABLED: pszGroup = "enabled" ; cch = sizeof("enabled" ) - 1; break;2206 case RTLOGGRPFLAGS_LEVEL_1: pszGroup = "level 1" ; cch = sizeof("level 1" ) - 1; break;2207 case RTLOGGRPFLAGS_LEVEL_2: pszGroup = "level 2" ; cch = sizeof("level 2" ) - 1; break;2208 case RTLOGGRPFLAGS_LEVEL_3: pszGroup = "level 3" ; cch = sizeof("level 3" ) - 1; break;2209 case RTLOGGRPFLAGS_LEVEL_4: pszGroup = "level 4" ; cch = sizeof("level 4" ) - 1; break;2210 case RTLOGGRPFLAGS_LEVEL_5: pszGroup = "level 5" ; cch = sizeof("level 5" ) - 1; break;2211 case RTLOGGRPFLAGS_LEVEL_6: pszGroup = "level 6" ; cch = sizeof("level 6" ) - 1; break;2212 case RTLOGGRPFLAGS_FLOW: pszGroup = "flow" ; cch = sizeof("flow" ) - 1; break;2213 2214 /* personal groups */2215 case RTLOGGRPFLAGS_LELIK: pszGroup = "lelik" ; cch = sizeof("lelik" ) - 1; break;2216 case RTLOGGRPFLAGS_MICHAEL: pszGroup = "Michael" ; cch = sizeof("Michael" ) - 1; break;2217 case RTLOGGRPFLAGS_DMIK: pszGroup = "dmik" ; cch = sizeof("dmik" ) - 1; break;2218 case RTLOGGRPFLAGS_SUNLOVER: pszGroup = "sunlover"; cch = sizeof("sunlover") - 1; break;2219 case RTLOGGRPFLAGS_ACHIM: pszGroup = "Achim" ; cch = sizeof("Achim" ) - 1; break;2220 case RTLOGGRPFLAGS_SANDER: pszGroup = "Sander" ; cch = sizeof("Sander" ) - 1; break;2221 case RTLOGGRPFLAGS_KLAUS: pszGroup = "Klaus" ; cch = sizeof("Klaus" ) - 1; break;2222 case RTLOGGRPFLAGS_FRANK: pszGroup = "Frank" ; cch = sizeof("Frank" ) - 1; break;2223 case RTLOGGRPFLAGS_BIRD: pszGroup = "bird" ; cch = sizeof("bird" ) - 1; break;2224 case RTLOGGRPFLAGS_NONAME: pszGroup = "noname" ; cch = sizeof("noname" ) - 1; break;2225 default: pszGroup = "????????"; cch = sizeof("????????") - 1; break;2226 }2227 if (pszGroup)2228 {2229 cch = RT_MIN(cch, 16);2230 memcpy(psz, pszGroup, cch);2231 psz += cch;2232 }2233 do2234 *psz++ = ' ';2235 while (cch++ < 8); /* +17 */2236 }2237 2238 /*2239 * Done, figure what we've used and advance the buffer and free size.2240 */2241 cb = psz - &pLogger->achScratch[pLogger->offScratch];2242 Assert(cb <= 198);2243 pLogger->offScratch += cb;2244 cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;2245 }2246 else if (cb <= 0)2247 {2248 rtlogFlush(pLogger);2249 cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;2250 }2251 2252 #if defined(DEBUG) && defined(IN_RING3)2253 /* sanity */2254 if (pLogger->offScratch >= sizeof(pLogger->achScratch))2255 {2256 fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",2257 pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));2258 AssertBreakpoint(); AssertBreakpoint();2259 }2260 #endif2261 2262 /* how much */2263 if (cb > cbChars)2264 cb = cbChars;2265 2266 /* have newline? */2267 pszNewLine = (const char *)memchr(pachChars, '\n', cb);2268 if (pszNewLine)2269 {2270 if (pLogger->fFlags & RTLOGFLAGS_USECRLF)2271 cb = pszNewLine - pachChars;2272 else2273 {2274 cb = pszNewLine - pachChars + 1;2275 pLogger->fPendingPrefix = true;2276 }2277 }2278 2279 /* copy */2280 memcpy(&pLogger->achScratch[pLogger->offScratch], pachChars, cb);2281 2282 /* advance */2283 pLogger->offScratch += cb;2284 cbRet += cb;2285 cbChars -= cb;2286 2287 if ( pszNewLine2288 && (pLogger->fFlags & RTLOGFLAGS_USECRLF)2289 && pLogger->offScratch + 2 < sizeof(pLogger->achScratch))2290 {2291 memcpy(&pLogger->achScratch[pLogger->offScratch], "\r\n", 2);2292 pLogger->offScratch += 2;2293 cbRet++;2294 cbChars--;2295 cb++;2296 pLogger->fPendingPrefix = true;2297 }2298 2299 /* done? */2300 if (cbChars <= 0)2301 return cbRet;2302 pachChars += cb;2303 }2304 2305 /* won't ever get here! */2306 }2307 else2308 {2309 /*2310 * Termination call.2311 * There's always space for a terminator, and it's not counted.2312 */2313 pLogger->achScratch[pLogger->offScratch] = '\0';2314 return 0;2315 }2316 }2317 -
trunk/src/VBox/Runtime/common/log/logrelellipsis.cpp
r12036 r12099 1 1 /* $Id$ */ 2 2 /** @file 3 * Runtime VBox - Logger .3 * Runtime VBox - Logger, the release ellipsis variants. 4 4 */ 5 5 … … 29 29 */ 30 30 31 32 31 /******************************************************************************* 33 32 * Header Files * 34 33 *******************************************************************************/ 35 34 #include <iprt/log.h> 36 #ifndef IN_GC37 # include <iprt/alloc.h>38 # include <iprt/process.h>39 # include <iprt/semaphore.h>40 # include <iprt/thread.h>41 # include <iprt/mp.h>42 #endif43 #ifdef IN_RING344 # include <iprt/file.h>45 # include <iprt/path.h>46 #endif47 #include <iprt/time.h>48 #include <iprt/asm.h>49 #include <iprt/assert.h>50 #include <iprt/err.h>51 #include <iprt/param.h>52 53 35 #include <iprt/stdarg.h> 54 #include <iprt/string.h>55 #include <iprt/ctype.h>56 #ifdef IN_RING357 # include <iprt/alloca.h>58 # include <stdio.h>59 #endif60 61 62 /*******************************************************************************63 * Structures and Typedefs *64 *******************************************************************************/65 /**66 * Arguments passed to the output function.67 */68 typedef struct RTLOGOUTPUTPREFIXEDARGS69 {70 /** The logger instance. */71 PRTLOGGER pLogger;72 /** The flags. (used for prefixing.) */73 unsigned fFlags;74 /** The group. (used for prefixing.) */75 unsigned iGroup;76 } RTLOGOUTPUTPREFIXEDARGS, *PRTLOGOUTPUTPREFIXEDARGS;77 78 79 /*******************************************************************************80 * Internal Functions *81 *******************************************************************************/82 #ifndef IN_GC83 static unsigned rtlogGroupFlags(const char *psz);84 #endif85 static void rtlogLogger(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args);86 static void rtlogFlush(PRTLOGGER pLogger);87 static DECLCALLBACK(size_t) rtLogOutput(void *pv, const char *pachChars, size_t cbChars);88 static DECLCALLBACK(size_t) rtLogOutputPrefixed(void *pv, const char *pachChars, size_t cbChars);89 90 91 /*******************************************************************************92 * Global Variables *93 *******************************************************************************/94 #ifdef IN_GC95 /** Default logger instance. */96 extern "C" DECLIMPORT(RTLOGGERRC) g_Logger;97 /** Default relese logger instance. */98 extern "C" DECLIMPORT(RTLOGGERRC) g_RelLogger;99 #else /* !IN_GC */100 /** Default logger instance. */101 static PRTLOGGER g_pLogger;102 /** Default release logger instance. */103 static PRTLOGGER g_pRelLogger;104 #endif /* !IN_GC */105 #ifdef IN_RING3106 /** The RTThreadGetWriteLockCount() change caused by the logger mutex semaphore. */107 static uint32_t volatile g_cLoggerLockCount;108 #endif109 #ifdef IN_RING0110 /** Number of per-thread loggers. */111 static int32_t volatile g_cPerThreadLoggers;112 /** Per-thread loggers.113 * This is just a quick TLS hack suitable for debug logging only.114 * If we run out of entries, just unload and reload the driver. */115 static struct RTLOGGERPERTHREAD116 {117 /** The thread. */118 RTNATIVETHREAD volatile NativeThread;119 /** The (process / session) key. */120 uintptr_t volatile uKey;121 /** The logger instance.*/122 PRTLOGGER volatile pLogger;123 } g_aPerThreadLoggers[8] =124 { { NIL_RTNATIVETHREAD, 0, 0},125 { NIL_RTNATIVETHREAD, 0, 0},126 { NIL_RTNATIVETHREAD, 0, 0},127 { NIL_RTNATIVETHREAD, 0, 0},128 { NIL_RTNATIVETHREAD, 0, 0},129 { NIL_RTNATIVETHREAD, 0, 0},130 { NIL_RTNATIVETHREAD, 0, 0},131 { NIL_RTNATIVETHREAD, 0, 0}132 };133 #endif /* IN_RING0 */134 135 136 /**137 * Locks the logger instance.138 *139 * @returns See RTSemFastMutexRequest().140 * @param pLogger The logger instance.141 */142 DECLINLINE(int) rtlogLock(PRTLOGGER pLogger)143 {144 #ifndef IN_GC145 if (pLogger->MutexSem != NIL_RTSEMFASTMUTEX)146 {147 int rc = RTSemFastMutexRequest(pLogger->MutexSem);148 AssertRCReturn(rc, rc);149 }150 #endif151 return VINF_SUCCESS;152 }153 154 155 /**156 * Unlocks the logger instance.157 * @param pLogger The logger instance.158 */159 DECLINLINE(void) rtlogUnlock(PRTLOGGER pLogger)160 {161 #ifndef IN_GC162 if (pLogger->MutexSem != NIL_RTSEMFASTMUTEX)163 RTSemFastMutexRelease(pLogger->MutexSem);164 #endif165 return;166 }167 168 169 #ifndef IN_GC170 /**171 * Create a logger instance, comprehensive version.172 *173 * @returns iprt status code.174 *175 * @param ppLogger Where to store the logger instance.176 * @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.177 * @param pszGroupSettings The initial group settings.178 * @param pszEnvVarBase Base name for the environment variables for this instance.179 * @param cGroups Number of groups in the array.180 * @param papszGroups Pointer to array of groups. This must stick around for the life of the181 * logger instance.182 * @param fDestFlags The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.183 * @param pszErrorMsg A buffer which is filled with an error message if something fails. May be NULL.184 * @param cchErrorMsg The size of the error message buffer.185 * @param pszFilenameFmt Log filename format string. Standard RTStrFormat().186 * @param ... Format arguments.187 */188 RTDECL(int) RTLogCreateExV(PRTLOGGER *ppLogger, RTUINT fFlags, const char *pszGroupSettings,189 const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,190 RTUINT fDestFlags, char *pszErrorMsg, size_t cchErrorMsg, const char *pszFilenameFmt, va_list args)191 {192 int rc;193 size_t cb;194 PRTLOGGER pLogger;195 196 /*197 * Validate input.198 */199 if ( (cGroups && !papszGroups)200 || !VALID_PTR(ppLogger)201 )202 {203 AssertMsgFailed(("Invalid parameters!\n"));204 return VERR_INVALID_PARAMETER;205 }206 *ppLogger = NULL;207 208 if (pszErrorMsg)209 RTStrPrintf(pszErrorMsg, cchErrorMsg, "unknown error");210 211 /*212 * Allocate a logger instance.213 */214 cb = RT_OFFSETOF(RTLOGGER, afGroups[cGroups + 1]) + RTPATH_MAX;215 pLogger = (PRTLOGGER)RTMemAllocZ(cb);216 if (pLogger)217 {218 uint8_t *pu8Code;219 220 pLogger->u32Magic = RTLOGGER_MAGIC;221 pLogger->papszGroups = papszGroups;222 pLogger->cMaxGroups = cGroups;223 pLogger->cGroups = cGroups;224 pLogger->pszFilename = (char *)&pLogger->afGroups[cGroups + 1];225 pLogger->File = NIL_RTFILE;226 pLogger->fFlags = fFlags;227 pLogger->fDestFlags = fDestFlags;228 pLogger->fPendingPrefix = true;229 if (pszGroupSettings)230 RTLogGroupSettings(pLogger, pszGroupSettings);231 232 /*233 * Emit wrapper code.234 */235 pu8Code = (uint8_t *)RTMemExecAlloc(64);236 if (pu8Code)237 {238 pLogger->pfnLogger = *(PFNRTLOGGER*)&pu8Code;239 #ifdef RT_ARCH_AMD64240 /* this wrapper will not be used on AMD64, we will be requiring C99 compilers there. */241 *pu8Code++ = 0xcc;242 #else243 *pu8Code++ = 0x68; /* push imm32 */244 *(void **)pu8Code = pLogger;245 pu8Code += sizeof(void *);246 *pu8Code++ = 0xe8; /* call rel32 */247 *(uint32_t *)pu8Code = (uintptr_t)RTLogLogger - ((uintptr_t)pu8Code + sizeof(uint32_t));248 pu8Code += sizeof(uint32_t);249 *pu8Code++ = 0x8d; /* lea esp, [esp + 4] */250 *pu8Code++ = 0x64;251 *pu8Code++ = 0x24;252 *pu8Code++ = 0x04;253 *pu8Code++ = 0xc3; /* ret near */254 #endif255 AssertMsg((uintptr_t)pu8Code - (uintptr_t)pLogger->pfnLogger <= 64,256 ("Wrapper assembly is too big! %d bytes\n", (uintptr_t)pu8Code - (uintptr_t)pLogger->pfnLogger));257 258 259 #ifdef IN_RING3 /* files and env.vars. are only accessible when in R3 at the present time. */260 /*261 * Format the filename.262 */263 if (pszFilenameFmt)264 {265 RTStrPrintfV(pLogger->pszFilename, RTPATH_MAX, pszFilenameFmt, args);266 pLogger->fDestFlags |= RTLOGDEST_FILE;267 }268 269 /*270 * Parse the environment variables.271 */272 if (pszEnvVarBase)273 {274 /* make temp copy of environment variable base. */275 size_t cchEnvVarBase = strlen(pszEnvVarBase);276 char *pszEnvVar = (char *)alloca(cchEnvVarBase + 16);277 memcpy(pszEnvVar, pszEnvVarBase, cchEnvVarBase);278 279 /*280 * Destination.281 */282 strcpy(pszEnvVar + cchEnvVarBase, "_DEST");283 const char *pszVar = getenv(pszEnvVar);284 if (pszVar)285 {286 while (*pszVar)287 {288 /* skip blanks. */289 while (RT_C_IS_SPACE(*pszVar))290 pszVar++;291 if (!*pszVar)292 break;293 294 /* parse instruction. */295 static struct296 {297 const char *pszInstr;298 unsigned fFlag;299 } const aDest[] =300 {301 { "file", RTLOGDEST_FILE }, /* Must be 1st! */302 { "dir", RTLOGDEST_FILE }, /* Must be 2nd! */303 { "stdout", RTLOGDEST_STDOUT },304 { "stderr", RTLOGDEST_STDERR },305 { "debugger", RTLOGDEST_DEBUGGER },306 { "com", RTLOGDEST_COM },307 { "user", RTLOGDEST_USER },308 };309 310 /* check no prefix. */311 bool fNo = false;312 if (pszVar[0] == 'n' && pszVar[1] == 'o')313 {314 fNo = true;315 pszVar += 2;316 }317 318 /* instruction. */319 unsigned i;320 for (i = 0; i < ELEMENTS(aDest); i++)321 {322 size_t cchInstr = strlen(aDest[i].pszInstr);323 if (!strncmp(pszVar, aDest[i].pszInstr, cchInstr))324 {325 if (!fNo)326 pLogger->fDestFlags |= aDest[i].fFlag;327 else328 pLogger->fDestFlags &= ~aDest[i].fFlag;329 pszVar += cchInstr;330 331 /* check for value. */332 while (RT_C_IS_SPACE(*pszVar))333 pszVar++;334 if (*pszVar == '=' || *pszVar == ':')335 {336 pszVar++;337 const char *pszEnd = strchr(pszVar, ';');338 if (!pszEnd)339 pszEnd = strchr(pszVar, '\0');340 341 /* log file name */342 size_t cch = pszEnd - pszVar;343 if (i == 0 /* file */ && !fNo)344 {345 memcpy(pLogger->pszFilename, pszVar, cch);346 pLogger->pszFilename[cch] = '\0';347 }348 /* log directory */349 else if (i == 1 /* dir */ && !fNo)350 {351 char szTmp[RTPATH_MAX];352 const char *pszFile = RTPathFilename(pLogger->pszFilename);353 if (pszFile)354 strcpy(szTmp, pszFile);355 else356 pszFile = ""; /* you've screwed up, sir. */357 358 memcpy(pLogger->pszFilename, pszVar, cch);359 pLogger->pszFilename[cch] = '\0';360 RTPathStripTrailingSlash(pLogger->pszFilename);361 362 cch = strlen(pLogger->pszFilename);363 pLogger->pszFilename[cch++] = '/';364 strcpy(&pLogger->pszFilename[cch], szTmp);365 }366 else367 AssertMsgFailed(("Invalid %s_DEST! %s%s doesn't take a value!\n", pszEnvVarBase, fNo ? "no" : "", aDest[i].pszInstr));368 pszVar = pszEnd + (*pszEnd != '\0');369 }370 break;371 }372 }373 /* unknown instruction? */374 if (i >= ELEMENTS(aDest))375 {376 AssertMsgFailed(("Invalid %s_DEST! unknown instruction %.20s\n", pszEnvVarBase, pszVar));377 pszVar++;378 }379 380 /* skip blanks and delimiters. */381 while (RT_C_IS_SPACE(*pszVar) || *pszVar == ';')382 pszVar++;383 } /* while more environment variable value left */384 }385 386 /*387 * The flags.388 */389 strcpy(pszEnvVar + cchEnvVarBase, "_FLAGS");390 pszVar = getenv(pszEnvVar);391 if (pszVar)392 RTLogFlags(pLogger, pszVar);393 394 /*395 * The group settings.396 */397 pszEnvVar[cchEnvVarBase] = '\0';398 pszVar = getenv(pszEnvVar);399 if (pszVar)400 RTLogGroupSettings(pLogger, pszVar);401 }402 #endif /* IN_RING3 */403 404 /*405 * Open the destination(s).406 */407 rc = VINF_SUCCESS;408 #ifdef IN_RING3409 if (pLogger->fDestFlags & RTLOGDEST_FILE)410 {411 if (!(pLogger->fFlags & RTLOGFLAGS_APPEND))412 rc = RTFileOpen(&pLogger->File, pLogger->pszFilename,413 RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE);414 else415 {416 /** @todo RTFILE_O_APPEND. */417 rc = RTFileOpen(&pLogger->File, pLogger->pszFilename,418 RTFILE_O_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE);419 if (RT_SUCCESS(rc))420 {421 rc = RTFileSeek(pLogger->File, 0, RTFILE_SEEK_END, NULL);422 if (RT_FAILURE(rc))423 {424 RTFileClose(pLogger->File);425 pLogger->File = NIL_RTFILE;426 }427 }428 }429 if (RT_FAILURE(rc) && pszErrorMsg)430 RTStrPrintf(pszErrorMsg, cchErrorMsg, "could not open file '%s'", pLogger->pszFilename);431 }432 #endif /* IN_RING3 */433 434 /*435 * Create mutex and check how much it counts when entering the lock436 * so that we can report the values for RTLOGFLAGS_PREFIX_LOCK_COUNTS.437 */438 if (RT_SUCCESS(rc))439 {440 rc = RTSemFastMutexCreate(&pLogger->MutexSem);441 if (RT_SUCCESS(rc))442 {443 #ifdef IN_RING3 /** @todo do counters in ring-0 too? */444 RTTHREAD Thread = RTThreadSelf();445 if (Thread != NIL_RTTHREAD)446 {447 int32_t c = RTThreadGetWriteLockCount(Thread);448 RTSemFastMutexRequest(pLogger->MutexSem);449 c = RTThreadGetWriteLockCount(Thread) - c;450 RTSemFastMutexRelease(pLogger->MutexSem);451 ASMAtomicWriteU32(&g_cLoggerLockCount, c);452 }453 #endif454 *ppLogger = pLogger;455 return VINF_SUCCESS;456 }457 458 if (pszErrorMsg)459 RTStrPrintf(pszErrorMsg, cchErrorMsg, "failed to create sempahore");460 }461 #ifdef IN_RING3462 RTFileClose(pLogger->File);463 #endif464 RTMemExecFree(*(void **)&pLogger->pfnLogger);465 }466 else467 {468 #ifdef RT_OS_LINUX469 /*470 * RTMemAlloc() succeeded but RTMemExecAlloc() failed -- most probably an SELinux problem.471 */472 if (pszErrorMsg)473 RTStrPrintf(pszErrorMsg, cchErrorMsg, "mmap(PROT_WRITE | PROT_EXEC) failed -- SELinux?");474 #endif /* RT_OS_LINUX */475 rc = VERR_NO_MEMORY;476 }477 RTMemFree(pLogger);478 }479 else480 rc = VERR_NO_MEMORY;481 482 return rc;483 }484 485 /**486 * Create a logger instance.487 *488 * @returns iprt status code.489 *490 * @param ppLogger Where to store the logger instance.491 * @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.492 * @param pszGroupSettings The initial group settings.493 * @param pszEnvVarBase Base name for the environment variables for this instance.494 * @param cGroups Number of groups in the array.495 * @param papszGroups Pointer to array of groups. This must stick around for the life of the496 * logger instance.497 * @param fDestFlags The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.498 * @param pszFilenameFmt Log filename format string. Standard RTStrFormat().499 * @param ... Format arguments.500 */501 RTDECL(int) RTLogCreate(PRTLOGGER *ppLogger, RTUINT fFlags, const char *pszGroupSettings,502 const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,503 RTUINT fDestFlags, const char *pszFilenameFmt, ...)504 {505 va_list args;506 int rc;507 508 va_start(args, pszFilenameFmt);509 rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups, fDestFlags, NULL, 0, pszFilenameFmt, args);510 va_end(args);511 return rc;512 }513 514 /**515 * Create a logger instance.516 *517 * @returns iprt status code.518 *519 * @param ppLogger Where to store the logger instance.520 * @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.521 * @param pszGroupSettings The initial group settings.522 * @param pszEnvVarBase Base name for the environment variables for this instance.523 * @param cGroups Number of groups in the array.524 * @param papszGroups Pointer to array of groups. This must stick around for the life of the525 * logger instance.526 * @param fDestFlags The destination flags. RTLOGDEST_FILE is ORed if pszFilenameFmt specified.527 * @param pszErrorMsg A buffer which is filled with an error message if something fails. May be NULL.528 * @param cchErrorMsg The size of the error message buffer.529 * @param pszFilenameFmt Log filename format string. Standard RTStrFormat().530 * @param ... Format arguments.531 */532 RTDECL(int) RTLogCreateEx(PRTLOGGER *ppLogger, RTUINT fFlags, const char *pszGroupSettings,533 const char *pszEnvVarBase, unsigned cGroups, const char * const * papszGroups,534 RTUINT fDestFlags, char *pszErrorMsg, size_t cchErrorMsg, const char *pszFilenameFmt, ...)535 {536 va_list args;537 int rc;538 539 va_start(args, pszFilenameFmt);540 rc = RTLogCreateExV(ppLogger, fFlags, pszGroupSettings, pszEnvVarBase, cGroups, papszGroups, fDestFlags, pszErrorMsg, cchErrorMsg, pszFilenameFmt, args);541 va_end(args);542 return rc;543 }544 545 /**546 * Destroys a logger instance.547 *548 * The instance is flushed and all output destinations closed (where applicable).549 *550 * @returns iprt status code.551 * @param pLogger The logger instance which close destroyed. NULL is fine.552 */553 RTDECL(int) RTLogDestroy(PRTLOGGER pLogger)554 {555 int rc;556 RTUINT iGroup;557 RTSEMFASTMUTEX MutexSem;558 559 /*560 * Validate input.561 */562 if (!pLogger)563 return VINF_SUCCESS;564 AssertReturn(VALID_PTR(pLogger), VERR_INVALID_POINTER);565 AssertReturn(pLogger->u32Magic == RTLOGGER_MAGIC, VERR_INVALID_MAGIC);566 567 /*568 * Acquire logger instance sem and disable all logging. (paranoia)569 */570 rc = rtlogLock(pLogger);571 if (RT_FAILURE(rc))572 return rc;573 574 pLogger->fFlags |= RTLOGFLAGS_DISABLED;575 iGroup = pLogger->cGroups;576 while (iGroup-- > 0)577 pLogger->afGroups[iGroup] = 0;578 579 /*580 * Flush it.581 */582 RTLogFlush(pLogger);583 584 /*585 * Close output stuffs.586 */587 #ifdef IN_RING3588 if (pLogger->File != NIL_RTFILE)589 {590 int rc2 = RTFileClose(pLogger->File);591 AssertRC(rc2);592 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))593 rc = rc2;594 pLogger->File = NIL_RTFILE;595 }596 #endif597 598 /*599 * Free the mutex and the instance memory.600 */601 MutexSem = pLogger->MutexSem;602 pLogger->MutexSem = NIL_RTSEMFASTMUTEX;603 if (MutexSem != NIL_RTSEMFASTMUTEX)604 {605 int rc2;606 RTSemFastMutexRelease(MutexSem);607 rc2 = RTSemFastMutexDestroy(MutexSem);608 AssertRC(rc2);609 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))610 rc = rc2;611 }612 613 RTMemFree(pLogger);614 615 return rc;616 }617 618 619 /**620 * Create a logger instance clone for RC usage.621 *622 * @returns iprt status code.623 *624 * @param pLogger The logger instance to be cloned.625 * @param pLoggerGC Where to create the GC logger instance.626 * @param cbLoggerGC Amount of memory allocated to for the GC logger instance clone.627 * @param pfnLoggerGCPtr Pointer to logger wrapper function for this instance (GC Ptr).628 * @param pfnFlushGCPtr Pointer to flush function (GC Ptr).629 * @param fFlags Logger instance flags, a combination of the RTLOGFLAGS_* values.630 */631 RTDECL(int) RTLogCloneRC(PRTLOGGER pLogger, PRTLOGGERRC pLoggerGC, size_t cbLoggerGC,632 RTRCPTR pfnLoggerGCPtr, RTRCPTR pfnFlushGCPtr, RTUINT fFlags)633 {634 /*635 * Validate input.636 */637 if ( !pLoggerGC638 || !pfnFlushGCPtr639 || !pfnLoggerGCPtr)640 {641 AssertMsgFailed(("Invalid parameters!\n"));642 return VERR_INVALID_PARAMETER;643 }644 if (cbLoggerGC < sizeof(*pLoggerGC))645 {646 AssertMsgFailed(("%d min=%d\n", cbLoggerGC, sizeof(*pLoggerGC)));647 return VERR_INVALID_PARAMETER;648 }649 650 /*651 * Initialize GC instance.652 */653 pLoggerGC->offScratch = 0;654 pLoggerGC->fPendingPrefix = false;655 pLoggerGC->pfnLogger = pfnLoggerGCPtr;656 pLoggerGC->pfnFlush = pfnFlushGCPtr;657 pLoggerGC->u32Magic = RTLOGGERRC_MAGIC;658 pLoggerGC->fFlags = fFlags | RTLOGFLAGS_DISABLED;659 pLoggerGC->cGroups = 1;660 pLoggerGC->afGroups[0] = 0;661 662 /*663 * Resolve defaults.664 */665 if (!pLogger)666 {667 pLogger = RTLogDefaultInstance();668 if (!pLogger)669 return VINF_SUCCESS;670 }671 672 /*673 * Check if there's enough space for the groups.674 */675 if (cbLoggerGC < (size_t)RT_OFFSETOF(RTLOGGERRC, afGroups[pLogger->cGroups]))676 {677 AssertMsgFailed(("%d req=%d cGroups=%d\n", cbLoggerGC, RT_OFFSETOF(RTLOGGERRC, afGroups[pLogger->cGroups]), pLogger->cGroups));678 return VERR_INVALID_PARAMETER;679 }680 memcpy(&pLoggerGC->afGroups[0], &pLogger->afGroups[0], pLogger->cGroups * sizeof(pLoggerGC->afGroups[0]));681 pLoggerGC->cGroups = pLogger->cGroups;682 683 /*684 * Copy bits from the HC instance.685 */686 pLoggerGC->fPendingPrefix = pLogger->fPendingPrefix;687 pLoggerGC->fFlags |= pLogger->fFlags;688 689 /*690 * Check if we can remove the disabled flag.691 */692 if ( pLogger->fDestFlags693 && !((pLogger->fFlags | fFlags) & RTLOGFLAGS_DISABLED))694 pLoggerGC->fFlags &= ~RTLOGFLAGS_DISABLED;695 696 return VINF_SUCCESS;697 }698 699 700 /**701 * Flushes a GC logger instance to a HC logger.702 *703 *704 * @returns iprt status code.705 * @param pLogger The HC logger instance to flush pLoggerGC to.706 * If NULL the default logger is used.707 * @param pLoggerGC The GC logger instance to flush.708 */709 RTDECL(void) RTLogFlushGC(PRTLOGGER pLogger, PRTLOGGERRC pLoggerGC)710 {711 /*712 * Resolve defaults.713 */714 if (!pLogger)715 {716 pLogger = RTLogDefaultInstance();717 if (!pLogger)718 {719 pLoggerGC->offScratch = 0;720 return;721 }722 }723 724 /*725 * Any thing to flush?726 */727 if ( pLogger->offScratch728 || pLoggerGC->offScratch)729 {730 /*731 * Acquire logger instance sem.732 */733 int rc = rtlogLock(pLogger);734 if (RT_FAILURE(rc))735 return;736 737 /*738 * Write whatever the GC instance contains to the HC one, and then739 * flush the HC instance.740 */741 if (pLoggerGC->offScratch)742 {743 rtLogOutput(pLogger, pLoggerGC->achScratch, pLoggerGC->offScratch);744 rtLogOutput(pLogger, NULL, 0);745 pLoggerGC->offScratch = 0;746 }747 748 /*749 * Release the semaphore.750 */751 rtlogUnlock(pLogger);752 }753 }754 755 756 #ifdef IN_RING3757 /**758 * Create a logger instance for singled threaded ring-0 usage.759 *760 * @returns iprt status code.761 *762 * @param pLogger Where to create the logger instance.763 * @param cbLogger The amount of memory available for the logger instance.764 * @param pfnLogger Pointer to logger wrapper function for the clone.765 * @param pfnFlush Pointer to flush function for the clone.766 * @param fFlags Logger instance flags for the clone, a combination of the RTLOGFLAGS_* values.767 * @param fDestFlags The destination flags.768 */769 RTDECL(int) RTLogCreateForR0(PRTLOGGER pLogger, size_t cbLogger, PFNRTLOGGER pfnLogger, PFNRTLOGFLUSH pfnFlush, RTUINT fFlags, RTUINT fDestFlags)770 {771 /*772 * Validate input.773 */774 AssertPtrReturn(pLogger, VERR_INVALID_PARAMETER);775 AssertReturn(cbLogger >= sizeof(*pLogger), VERR_INVALID_PARAMETER);776 AssertReturn(pfnLogger, VERR_INVALID_PARAMETER);777 AssertReturn(pfnFlush, VERR_INVALID_PARAMETER);778 779 /*780 * Initialize the ring-0 instance.781 */782 pLogger->offScratch = 0;783 pLogger->fPendingPrefix = false;784 pLogger->pfnLogger = pfnLogger;785 pLogger->pfnFlush = pfnFlush;786 pLogger->MutexSem = NIL_RTSEMFASTMUTEX; /* Not serialized. */787 pLogger->u32Magic = RTLOGGER_MAGIC;788 pLogger->fFlags = fFlags;789 pLogger->fDestFlags = fDestFlags & ~RTLOGDEST_FILE;790 pLogger->File = NIL_RTFILE;791 pLogger->pszFilename = NULL;792 pLogger->papszGroups = NULL;793 pLogger->cMaxGroups = (cbLogger - RT_OFFSETOF(RTLOGGER, afGroups[0])) / sizeof(pLogger->afGroups[0]);794 pLogger->cGroups = 1;795 pLogger->afGroups[0] = 0;796 return VINF_SUCCESS;797 }798 #endif /* IN_RING3 */799 800 801 /**802 * Copies the group settings and flags from logger instance to another.803 *804 * @returns IPRT status code.805 * @param pDstLogger The destination logger instance.806 * @param pSrcLogger The source logger instance. If NULL the default one is used.807 * @param fFlagsOr OR mask for the flags.808 * @param fFlagsAnd AND mask for the flags.809 */810 RTDECL(int) RTLogCopyGroupsAndFlags(PRTLOGGER pDstLogger, PCRTLOGGER pSrcLogger, unsigned fFlagsOr, unsigned fFlagsAnd)811 {812 int rc;813 unsigned cGroups;814 815 /*816 * Validate input.817 */818 AssertPtrReturn(pDstLogger, VERR_INVALID_PARAMETER);819 AssertPtrNullReturn(pSrcLogger, VERR_INVALID_PARAMETER);820 821 /*822 * Resolve defaults.823 */824 if (!pSrcLogger)825 {826 pSrcLogger = RTLogDefaultInstance();827 if (!pSrcLogger)828 {829 pDstLogger->fFlags |= RTLOGFLAGS_DISABLED;830 pDstLogger->cGroups = 1;831 pDstLogger->afGroups[0] = 0;832 return VINF_SUCCESS;833 }834 }835 836 /*837 * Copy flags and group settings.838 */839 pDstLogger->fFlags = (pSrcLogger->fFlags & fFlagsAnd) | fFlagsOr;840 841 rc = VINF_SUCCESS;842 cGroups = pSrcLogger->cGroups;843 if (cGroups < pDstLogger->cMaxGroups)844 {845 AssertMsgFailed(("cMaxGroups=%zd cGroups=%zd (min size %d)\n", pDstLogger->cMaxGroups,846 pSrcLogger->cGroups, RT_OFFSETOF(RTLOGGER, afGroups[pSrcLogger->cGroups])));847 rc = VERR_INVALID_PARAMETER;848 cGroups = pDstLogger->cMaxGroups;849 }850 memcpy(&pDstLogger->afGroups[0], &pSrcLogger->afGroups[0], cGroups * sizeof(pDstLogger->afGroups[0]));851 pDstLogger->cGroups = cGroups;852 853 return rc;854 }855 856 857 /**858 * Flushes the buffer in one logger instance onto another logger.859 *860 * @returns iprt status code.861 *862 * @param pSrcLogger The logger instance to flush.863 * @param pDstLogger The logger instance to flush onto.864 * If NULL the default logger will be used.865 */866 RTDECL(void) RTLogFlushToLogger(PRTLOGGER pSrcLogger, PRTLOGGER pDstLogger)867 {868 /*869 * Resolve defaults.870 */871 if (!pDstLogger)872 {873 pDstLogger = RTLogDefaultInstance();874 if (!pDstLogger)875 {876 /* flushing to "/dev/null". */877 if (pSrcLogger->offScratch)878 {879 int rc = rtlogLock(pSrcLogger);880 if (RT_SUCCESS(rc))881 {882 pSrcLogger->offScratch = 0;883 rtlogLock(pSrcLogger);884 }885 }886 return;887 }888 }889 890 /*891 * Any thing to flush?892 */893 if ( pSrcLogger->offScratch894 || pDstLogger->offScratch)895 {896 /*897 * Acquire logger semaphores.898 */899 int rc = rtlogLock(pDstLogger);900 if (RT_FAILURE(rc))901 return;902 rc = rtlogLock(pSrcLogger);903 if (RT_SUCCESS(rc))904 {905 /*906 * Write whatever the GC instance contains to the HC one, and then907 * flush the HC instance.908 */909 if (pSrcLogger->offScratch)910 {911 rtLogOutput(pDstLogger, pSrcLogger->achScratch, pSrcLogger->offScratch);912 rtLogOutput(pDstLogger, NULL, 0);913 pSrcLogger->offScratch = 0;914 }915 916 /*917 * Release the semaphores.918 */919 rtlogUnlock(pSrcLogger);920 }921 rtlogUnlock(pDstLogger);922 }923 }924 925 926 /**927 * Matches a group name with a pattern mask in an case insensitive manner (ASCII).928 *929 * @returns true if matching and *ppachMask set to the end of the pattern.930 * @returns false if no match.931 * @param pszGrp The group name.932 * @param ppachMask Pointer to the pointer to the mask. Only wildcard supported is '*'.933 * @param cchMask The length of the mask, including modifiers. The modifiers is why934 * we update *ppachMask on match.935 */936 static bool rtlogIsGroupMatching(const char *pszGrp, const char **ppachMask, unsigned cchMask)937 {938 const char *pachMask;939 940 if (!pszGrp || !*pszGrp)941 return false;942 pachMask = *ppachMask;943 for (;;)944 {945 if (RT_C_TO_LOWER(*pszGrp) != RT_C_TO_LOWER(*pachMask))946 {947 const char *pszTmp;948 949 /*950 * Check for wildcard and do a minimal match if found.951 */952 if (*pachMask != '*')953 return false;954 955 /* eat '*'s. */956 do pachMask++;957 while (--cchMask && *pachMask == '*');958 959 /* is there more to match? */960 if ( !cchMask961 || *pachMask == '.'962 || *pachMask == '=')963 break; /* we're good */964 965 /* do extremely minimal matching (fixme) */966 pszTmp = strchr(pszGrp, RT_C_TO_LOWER(*pachMask));967 if (!pszTmp)968 pszTmp = strchr(pszGrp, RT_C_TO_UPPER(*pachMask));969 if (!pszTmp)970 return false;971 pszGrp = pszTmp;972 continue;973 }974 975 /* done? */976 if (!*++pszGrp)977 {978 /* trailing wildcard is ok. */979 do980 {981 pachMask++;982 cchMask--;983 } while (cchMask && *pachMask == '*');984 if ( !cchMask985 || *pachMask == '.'986 || *pachMask == '=')987 break; /* we're good */988 return false;989 }990 991 if (!--cchMask)992 return false;993 pachMask++;994 }995 996 /* match */997 *ppachMask = pachMask;998 return true;999 }1000 1001 1002 /**1003 * Updates the group settings for the logger instance using the specified1004 * specification string.1005 *1006 * @returns iprt status code.1007 * Failures can safely be ignored.1008 * @param pLogger Logger instance.1009 * @param pszVar Value to parse.1010 */1011 RTDECL(int) RTLogGroupSettings(PRTLOGGER pLogger, const char *pszVar)1012 {1013 /*1014 * Resolve defaults.1015 */1016 if (!pLogger)1017 {1018 pLogger = RTLogDefaultInstance();1019 if (!pLogger)1020 return VINF_SUCCESS;1021 }1022 1023 /*1024 * Iterate the string.1025 */1026 while (*pszVar)1027 {1028 /*1029 * Skip prefixes (blanks, ;, + and -).1030 */1031 bool fEnabled = true;1032 char ch;1033 const char *pszStart;1034 unsigned i;1035 size_t cch;1036 1037 while ((ch = *pszVar) == '+' || ch == '-' || ch == ' ' || ch == '\t' || ch == '\n' || ch == ';')1038 {1039 if (ch == '+' || ch == '-' || ';')1040 fEnabled = ch != '-';1041 pszVar++;1042 }1043 if (!*pszVar)1044 break;1045 1046 /*1047 * Find end.1048 */1049 pszStart = pszVar;1050 while ((ch = *pszVar) != '\0' && ch != '+' && ch != '-' && ch != ' ' && ch != '\t')1051 pszVar++;1052 1053 /*1054 * Find the group (ascii case insensitive search).1055 * Special group 'all'.1056 */1057 cch = pszVar - pszStart;1058 if ( cch >= 31059 && (pszStart[0] == 'a' || pszStart[0] == 'A')1060 && (pszStart[1] == 'l' || pszStart[1] == 'L')1061 && (pszStart[2] == 'l' || pszStart[2] == 'L')1062 && (cch == 3 || pszStart[3] == '.' || pszStart[3] == '='))1063 {1064 /*1065 * All.1066 */1067 unsigned fFlags = cch == 31068 ? RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_11069 : rtlogGroupFlags(&pszStart[3]);1070 for (i = 0; i < pLogger->cGroups; i++)1071 {1072 if (fEnabled)1073 pLogger->afGroups[i] |= fFlags;1074 else1075 pLogger->afGroups[i] &= ~fFlags;1076 }1077 }1078 else1079 {1080 /*1081 * Specific group(s).1082 */1083 for (i = 0; i < pLogger->cGroups; i++)1084 {1085 const char *psz2 = (const char*)pszStart;1086 if (rtlogIsGroupMatching(pLogger->papszGroups[i], &psz2, cch))1087 {1088 unsigned fFlags = RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1;1089 if (*psz2 == '.' || *psz2 == '=')1090 fFlags = rtlogGroupFlags(psz2);1091 if (fEnabled)1092 pLogger->afGroups[i] |= fFlags;1093 else1094 pLogger->afGroups[i] &= ~fFlags;1095 }1096 } /* for each group */1097 }1098 1099 } /* parse specification */1100 1101 return VINF_SUCCESS;1102 }1103 1104 1105 /**1106 * Interprets the group flags suffix.1107 *1108 * @returns Flags specified. (0 is possible!)1109 * @param psz Start of Suffix. (Either dot or equal sign.)1110 */1111 static unsigned rtlogGroupFlags(const char *psz)1112 {1113 unsigned fFlags = 0;1114 1115 /*1116 * Litteral flags.1117 */1118 while (*psz == '.')1119 {1120 static struct1121 {1122 const char *pszFlag; /* lowercase!! */1123 unsigned fFlag;1124 } aFlags[] =1125 {1126 { "eo", RTLOGGRPFLAGS_ENABLED },1127 { "enabledonly",RTLOGGRPFLAGS_ENABLED },1128 { "e", RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1 },1129 { "enabled", RTLOGGRPFLAGS_ENABLED | RTLOGGRPFLAGS_LEVEL_1 },1130 { "l1", RTLOGGRPFLAGS_LEVEL_1 },1131 { "level1", RTLOGGRPFLAGS_LEVEL_1 },1132 { "l", RTLOGGRPFLAGS_LEVEL_2 },1133 { "l2", RTLOGGRPFLAGS_LEVEL_2 },1134 { "level2", RTLOGGRPFLAGS_LEVEL_2 },1135 { "l3", RTLOGGRPFLAGS_LEVEL_3 },1136 { "level3", RTLOGGRPFLAGS_LEVEL_3 },1137 { "l4", RTLOGGRPFLAGS_LEVEL_4 },1138 { "level4", RTLOGGRPFLAGS_LEVEL_4 },1139 { "l5", RTLOGGRPFLAGS_LEVEL_5 },1140 { "level5", RTLOGGRPFLAGS_LEVEL_5 },1141 { "l6", RTLOGGRPFLAGS_LEVEL_6 },1142 { "level6", RTLOGGRPFLAGS_LEVEL_6 },1143 { "f", RTLOGGRPFLAGS_FLOW },1144 { "flow", RTLOGGRPFLAGS_FLOW },1145 1146 { "lelik", RTLOGGRPFLAGS_LELIK },1147 { "michael", RTLOGGRPFLAGS_MICHAEL },1148 { "dmik", RTLOGGRPFLAGS_DMIK },1149 { "sunlover", RTLOGGRPFLAGS_SUNLOVER },1150 { "achim", RTLOGGRPFLAGS_ACHIM },1151 { "achimha", RTLOGGRPFLAGS_ACHIM },1152 { "s", RTLOGGRPFLAGS_SANDER },1153 { "sander", RTLOGGRPFLAGS_SANDER },1154 { "sandervl", RTLOGGRPFLAGS_SANDER },1155 { "klaus", RTLOGGRPFLAGS_KLAUS },1156 { "frank", RTLOGGRPFLAGS_FRANK },1157 { "b", RTLOGGRPFLAGS_BIRD },1158 { "bird", RTLOGGRPFLAGS_BIRD },1159 { "aleksey", RTLOGGRPFLAGS_ALEKSEY },1160 { "n", RTLOGGRPFLAGS_NONAME },1161 { "noname", RTLOGGRPFLAGS_NONAME }1162 };1163 unsigned i;1164 bool fFound = false;1165 psz++;1166 for (i = 0; i < ELEMENTS(aFlags) && !fFound; i++)1167 {1168 const char *psz1 = aFlags[i].pszFlag;1169 const char *psz2 = psz;1170 while (*psz1 == RT_C_TO_LOWER(*psz2))1171 {1172 psz1++;1173 psz2++;1174 if (!*psz1)1175 {1176 if ( (*psz2 >= 'a' && *psz2 <= 'z')1177 || (*psz2 >= 'A' && *psz2 <= 'Z')1178 || (*psz2 >= '0' && *psz2 <= '9') )1179 break;1180 fFlags |= aFlags[i].fFlag;1181 fFound = true;1182 psz = psz2;1183 break;1184 }1185 } /* strincmp */1186 } /* for each flags */1187 }1188 1189 /*1190 * Flag value.1191 */1192 if (*psz == '=')1193 {1194 psz++;1195 if (*psz == '~')1196 fFlags = ~RTStrToInt32(psz + 1);1197 else1198 fFlags = RTStrToInt32(psz);1199 }1200 1201 return fFlags;1202 }1203 1204 #endif /* !IN_GC */1205 1206 1207 /**1208 * Updates the flags for the logger instance using the specified1209 * specification string.1210 *1211 * @returns iprt status code.1212 * Failures can safely be ignored.1213 * @param pLogger Logger instance (NULL for default logger).1214 * @param pszVar Value to parse.1215 */1216 RTDECL(int) RTLogFlags(PRTLOGGER pLogger, const char *pszVar)1217 {1218 int rc = VINF_SUCCESS;1219 1220 /*1221 * Resolve defaults.1222 */1223 if (!pLogger)1224 {1225 pLogger = RTLogDefaultInstance();1226 if (!pLogger)1227 return VINF_SUCCESS;1228 }1229 1230 /*1231 * Iterate the string.1232 */1233 while (*pszVar)1234 {1235 /* parse instruction. */1236 static struct1237 {1238 const char *pszInstr;1239 size_t cchInstr;1240 unsigned fFlag;1241 bool fInverted;1242 } const aDest[] =1243 {1244 { "disabled", sizeof("disabled" ) - 1, RTLOGFLAGS_DISABLED, false },1245 { "enabled", sizeof("enabled" ) - 1, RTLOGFLAGS_DISABLED, true },1246 { "buffered", sizeof("buffered" ) - 1, RTLOGFLAGS_BUFFERED, false },1247 { "unbuffered", sizeof("unbuffered" ) - 1, RTLOGFLAGS_BUFFERED, true },1248 { "usecrlf", sizeof("usecrlf" ) - 1, RTLOGFLAGS_USECRLF, true },1249 { "uself", sizeof("uself" ) - 1, RTLOGFLAGS_USECRLF, false },1250 { "append", sizeof("append" ) - 1, RTLOGFLAGS_APPEND, false },1251 { "overwrite", sizeof("overwrite" ) - 1, RTLOGFLAGS_APPEND, true },1252 { "rel", sizeof("rel" ) - 1, RTLOGFLAGS_REL_TS, false },1253 { "abs", sizeof("abs" ) - 1, RTLOGFLAGS_REL_TS, true },1254 { "dec", sizeof("dec" ) - 1, RTLOGFLAGS_DECIMAL_TS, false },1255 { "hex", sizeof("hex" ) - 1, RTLOGFLAGS_DECIMAL_TS, true },1256 { "lockcnts", sizeof("lockcnts" ) - 1, RTLOGFLAGS_PREFIX_LOCK_COUNTS, false },1257 { "cpuid", sizeof("cpuid" ) - 1, RTLOGFLAGS_PREFIX_CPUID, false },1258 { "pid", sizeof("pid" ) - 1, RTLOGFLAGS_PREFIX_PID, false },1259 { "flagno", sizeof("flagno" ) - 1, RTLOGFLAGS_PREFIX_FLAG_NO, false },1260 { "flag", sizeof("flag" ) - 1, RTLOGFLAGS_PREFIX_FLAG, false },1261 { "groupno", sizeof("groupno" ) - 1, RTLOGFLAGS_PREFIX_GROUP_NO, false },1262 { "group", sizeof("group" ) - 1, RTLOGFLAGS_PREFIX_GROUP, false },1263 { "tid", sizeof("tid" ) - 1, RTLOGFLAGS_PREFIX_TID, false },1264 { "thread", sizeof("thread" ) - 1, RTLOGFLAGS_PREFIX_THREAD, false },1265 { "timeprog", sizeof("timeprog" ) - 1, RTLOGFLAGS_PREFIX_TIME_PROG, false },1266 { "time", sizeof("time" ) - 1, RTLOGFLAGS_PREFIX_TIME, false },1267 { "msprog", sizeof("msprog" ) - 1, RTLOGFLAGS_PREFIX_MS_PROG, false },1268 { "tsc", sizeof("tsc" ) - 1, RTLOGFLAGS_PREFIX_TSC, false }, /* before ts! */1269 { "ts", sizeof("ts" ) - 1, RTLOGFLAGS_PREFIX_TS, false },1270 };1271 1272 /* check no prefix. */1273 bool fNo = false;1274 char ch;1275 unsigned i;1276 1277 /* skip blanks. */1278 while (RT_C_IS_SPACE(*pszVar))1279 pszVar++;1280 if (!*pszVar)1281 return rc;1282 1283 while ((ch = *pszVar) != '\0')1284 {1285 if (ch == 'n' && pszVar[1] == 'o')1286 {1287 pszVar += 2;1288 fNo = !fNo;1289 }1290 else if (ch == '+')1291 {1292 pszVar++;1293 fNo = true;1294 }1295 else if (ch == '-' || ch == '!' || ch == '~')1296 {1297 pszVar++;1298 fNo = !fNo;1299 }1300 else1301 break;1302 }1303 1304 /* instruction. */1305 for (i = 0; i < ELEMENTS(aDest); i++)1306 {1307 if (!strncmp(pszVar, aDest[i].pszInstr, aDest[i].cchInstr))1308 {1309 if (fNo == aDest[i].fInverted)1310 pLogger->fFlags |= aDest[i].fFlag;1311 else1312 pLogger->fFlags &= ~aDest[i].fFlag;1313 pszVar += aDest[i].cchInstr;1314 break;1315 }1316 }1317 1318 /* unknown instruction? */1319 if (i >= ELEMENTS(aDest))1320 {1321 AssertMsgFailed(("Invalid flags! unknown instruction %.20s\n", pszVar));1322 pszVar++;1323 }1324 1325 /* skip blanks and delimiters. */1326 while (RT_C_IS_SPACE(*pszVar) || *pszVar == ';')1327 pszVar++;1328 } /* while more environment variable value left */1329 1330 return rc;1331 }1332 1333 1334 /**1335 * Flushes the specified logger.1336 *1337 * @param pLogger The logger instance to flush.1338 * If NULL the default instance is used. The default instance1339 * will not be initialized by this call.1340 */1341 RTDECL(void) RTLogFlush(PRTLOGGER pLogger)1342 {1343 /*1344 * Resolve defaults.1345 */1346 if (!pLogger)1347 {1348 #ifdef IN_GC1349 pLogger = &g_Logger;1350 #else1351 pLogger = g_pLogger;1352 #endif1353 if (!pLogger)1354 return;1355 }1356 1357 /*1358 * Any thing to flush?1359 */1360 if (pLogger->offScratch)1361 {1362 #ifndef IN_GC1363 /*1364 * Acquire logger instance sem.1365 */1366 int rc = rtlogLock(pLogger);1367 if (RT_FAILURE(rc))1368 return;1369 #endif1370 /*1371 * Call worker.1372 */1373 rtlogFlush(pLogger);1374 1375 #ifndef IN_GC1376 /*1377 * Release the semaphore.1378 */1379 rtlogUnlock(pLogger);1380 #endif1381 }1382 }1383 1384 1385 /**1386 * Gets the default logger instance.1387 *1388 * @returns Pointer to default logger instance.1389 * @returns NULL if no default logger instance available.1390 */1391 RTDECL(PRTLOGGER) RTLogDefaultInstance(void)1392 {1393 #ifdef IN_GC1394 return &g_Logger;1395 1396 #else /* !IN_GC */1397 # ifdef IN_RING01398 /*1399 * Check per thread loggers first.1400 */1401 if (g_cPerThreadLoggers)1402 {1403 const RTNATIVETHREAD Self = RTThreadNativeSelf();1404 int32_t i = ELEMENTS(g_aPerThreadLoggers);1405 while (i-- > 0)1406 if (g_aPerThreadLoggers[i].NativeThread == Self)1407 return g_aPerThreadLoggers[i].pLogger;1408 }1409 # endif /* IN_RING0 */1410 1411 /*1412 * If no per thread logger, use the default one.1413 */1414 if (!g_pLogger)1415 g_pLogger = RTLogDefaultInit();1416 return g_pLogger;1417 #endif /* !IN_GC */1418 }1419 1420 1421 #ifndef IN_GC1422 /**1423 * Sets the default logger instance.1424 *1425 * @returns iprt status code.1426 * @param pLogger The new default logger instance.1427 */1428 RTDECL(PRTLOGGER) RTLogSetDefaultInstance(PRTLOGGER pLogger)1429 {1430 return (PRTLOGGER)ASMAtomicXchgPtr((void * volatile *)&g_pLogger, pLogger);1431 }1432 #endif /* !IN_GC */1433 1434 1435 #ifdef IN_RING01436 /**1437 * Changes the default logger instance for the current thread.1438 *1439 * @returns IPRT status code.1440 * @param pLogger The logger instance. Pass NULL for deregistration.1441 * @param uKey Associated key for cleanup purposes. If pLogger is NULL,1442 * all instances with this key will be deregistered. So in1443 * order to only deregister the instance associated with the1444 * current thread use 0.1445 */1446 RTDECL(int) RTLogSetDefaultInstanceThread(PRTLOGGER pLogger, uintptr_t uKey)1447 {1448 int rc;1449 RTNATIVETHREAD Self = RTThreadNativeSelf();1450 if (pLogger)1451 {1452 int32_t i;1453 unsigned j;1454 1455 AssertReturn(pLogger->u32Magic == RTLOGGER_MAGIC, VERR_INVALID_MAGIC);1456 1457 /*1458 * Iterate the table to see if there is already an entry for this thread.1459 */1460 i = ELEMENTS(g_aPerThreadLoggers);1461 while (i-- > 0)1462 if (g_aPerThreadLoggers[i].NativeThread == Self)1463 {1464 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, (void *)uKey);1465 g_aPerThreadLoggers[i].pLogger = pLogger;1466 return VINF_SUCCESS;1467 }1468 1469 /*1470 * Allocate a new table entry.1471 */1472 i = ASMAtomicIncS32(&g_cPerThreadLoggers);1473 if (i > (int32_t)ELEMENTS(g_aPerThreadLoggers))1474 {1475 ASMAtomicDecS32(&g_cPerThreadLoggers);1476 return VERR_BUFFER_OVERFLOW; /* horrible error code! */1477 }1478 1479 for (j = 0; j < 10; j++)1480 {1481 i = ELEMENTS(g_aPerThreadLoggers);1482 while (i-- > 0)1483 {1484 AssertCompile(sizeof(RTNATIVETHREAD) == sizeof(void*));1485 if ( g_aPerThreadLoggers[i].NativeThread == NIL_RTNATIVETHREAD1486 && ASMAtomicCmpXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].NativeThread, (void *)Self, (void *)NIL_RTNATIVETHREAD))1487 {1488 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, (void *)uKey);1489 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].pLogger, pLogger);1490 return VINF_SUCCESS;1491 }1492 }1493 }1494 1495 ASMAtomicDecS32(&g_cPerThreadLoggers);1496 rc = VERR_INTERNAL_ERROR;1497 }1498 else1499 {1500 /*1501 * Search the array for the current thread.1502 */1503 int32_t i = ELEMENTS(g_aPerThreadLoggers);1504 while (i-- > 0)1505 if ( g_aPerThreadLoggers[i].NativeThread == Self1506 || g_aPerThreadLoggers[i].uKey == uKey)1507 {1508 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].uKey, NULL);1509 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].pLogger, NULL);1510 ASMAtomicXchgPtr((void * volatile *)&g_aPerThreadLoggers[i].NativeThread, (void *)NIL_RTNATIVETHREAD);1511 ASMAtomicDecS32(&g_cPerThreadLoggers);1512 }1513 1514 rc = VINF_SUCCESS;1515 }1516 return rc;1517 }1518 #endif1519 1520 1521 /**1522 * Gets the default release logger instance.1523 *1524 * @returns Pointer to default release logger instance.1525 * @returns NULL if no default release logger instance available.1526 */1527 RTDECL(PRTLOGGER) RTLogRelDefaultInstance(void)1528 {1529 #ifdef IN_GC1530 return &g_RelLogger;1531 #else /* !IN_GC */1532 return g_pRelLogger;1533 #endif /* !IN_GC */1534 }1535 1536 1537 #ifndef IN_GC1538 /**1539 * Sets the default logger instance.1540 *1541 * @returns iprt status code.1542 * @param pLogger The new default release logger instance.1543 */1544 RTDECL(PRTLOGGER) RTLogRelSetDefaultInstance(PRTLOGGER pLogger)1545 {1546 return (PRTLOGGER)ASMAtomicXchgPtr((void * volatile *)&g_pRelLogger, pLogger);1547 }1548 #endif /* !IN_GC */1549 1550 1551 /**1552 * Write to a logger instance.1553 *1554 * @param pLogger Pointer to logger instance.1555 * @param pvCallerRet Ignored.1556 * @param pszFormat Format string.1557 * @param ... Format arguments.1558 */1559 RTDECL(void) RTLogLogger(PRTLOGGER pLogger, void *pvCallerRet, const char *pszFormat, ...)1560 {1561 va_list args;1562 va_start(args, pszFormat);1563 #if defined(RT_OS_DARWIN) && defined(RT_ARCH_X86) && defined(IN_RING3)1564 /* manually align the stack before doing the call.1565 * We boldly assume that there is a stack frame here! */1566 __asm__ __volatile__("andl $-32, %%esp\t\n" ::: "%esp");1567 RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);1568 #else1569 RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);1570 #endif1571 va_end(args);1572 }1573 1574 1575 /**1576 * Write to a logger instance.1577 *1578 * @param pLogger Pointer to logger instance.1579 * @param pszFormat Format string.1580 * @param args Format arguments.1581 */1582 RTDECL(void) RTLogLoggerV(PRTLOGGER pLogger, const char *pszFormat, va_list args)1583 {1584 RTLogLoggerExV(pLogger, 0, ~0U, pszFormat, args);1585 }1586 1587 1588 /**1589 * Write to a logger instance.1590 *1591 * This function will check whether the instance, group and flags makes up a1592 * logging kind which is currently enabled before writing anything to the log.1593 *1594 * @param pLogger Pointer to logger instance. If NULL the default logger instance will be attempted.1595 * @param fFlags The logging flags.1596 * @param iGroup The group.1597 * The value ~0U is reserved for compatability with RTLogLogger[V] and is1598 * only for internal usage!1599 * @param pszFormat Format string.1600 * @param ... Format arguments.1601 * @remark This is a worker function of LogIt.1602 */1603 RTDECL(void) RTLogLoggerEx(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, ...)1604 {1605 va_list args;1606 va_start(args, pszFormat);1607 RTLogLoggerExV(pLogger, fFlags, iGroup, pszFormat, args);1608 va_end(args);1609 }1610 1611 1612 /**1613 * Write to a logger instance.1614 *1615 * This function will check whether the instance, group and flags makes up a1616 * logging kind which is currently enabled before writing anything to the log.1617 *1618 * @param pLogger Pointer to logger instance. If NULL the default logger instance will be attempted.1619 * @param fFlags The logging flags.1620 * @param iGroup The group.1621 * The value ~0U is reserved for compatability with RTLogLogger[V] and is1622 * only for internal usage!1623 * @param pszFormat Format string.1624 * @param args Format arguments.1625 */1626 RTDECL(void) RTLogLoggerExV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)1627 {1628 /*1629 * A NULL logger means default instance.1630 */1631 if (!pLogger)1632 {1633 pLogger = RTLogDefaultInstance();1634 if (!pLogger)1635 return;1636 }1637 rtlogLogger(pLogger, fFlags, iGroup, pszFormat, args);1638 }1639 36 1640 37 … … 1664 61 1665 62 /** 1666 * Write to a logger instance, defaulting to the release one.1667 *1668 * This function will check whether the instance, group and flags makes up a1669 * logging kind which is currently enabled before writing anything to the log.1670 *1671 * @param pLogger Pointer to logger instance. If NULL the default release instance is attempted.1672 * @param fFlags The logging flags.1673 * @param iGroup The group.1674 * The value ~0U is reserved for compatability with RTLogLogger[V] and is1675 * only for internal usage!1676 * @param pszFormat Format string.1677 * @param args Format arguments.1678 */1679 RTDECL(void) RTLogRelLoggerV(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)1680 {1681 /*1682 * A NULL logger means default instance.1683 */1684 if (!pLogger)1685 {1686 pLogger = RTLogRelDefaultInstance();1687 if (!pLogger)1688 return;1689 }1690 rtlogLogger(pLogger, fFlags, iGroup, pszFormat, args);1691 }1692 1693 1694 /**1695 * Worker for the RTLog[Rel]Logger*() functions.1696 *1697 * @param pLogger Pointer to logger instance.1698 * @param fFlags The logging flags.1699 * @param iGroup The group.1700 * The value ~0U is reserved for compatability with RTLogLogger[V] and is1701 * only for internal usage!1702 * @param pszFormat Format string.1703 * @param args Format arguments.1704 */1705 static void rtlogLogger(PRTLOGGER pLogger, unsigned fFlags, unsigned iGroup, const char *pszFormat, va_list args)1706 {1707 int rc;1708 1709 /*1710 * Validate and correct iGroup.1711 */1712 if (iGroup != ~0U && iGroup >= pLogger->cGroups)1713 iGroup = 0;1714 1715 /*1716 * If no output, then just skip it.1717 */1718 if ( (pLogger->fFlags & RTLOGFLAGS_DISABLED)1719 #ifndef IN_GC1720 || !pLogger->fDestFlags1721 #endif1722 || !pszFormat || !*pszFormat)1723 return;1724 if ( iGroup != ~0U1725 && (pLogger->afGroups[iGroup] & (fFlags | RTLOGGRPFLAGS_ENABLED)) != (fFlags | RTLOGGRPFLAGS_ENABLED))1726 return;1727 1728 /*1729 * Acquire logger instance sem.1730 */1731 rc = rtlogLock(pLogger);1732 if (RT_FAILURE(rc))1733 return;1734 1735 /*1736 * Format the message and perhaps flush it.1737 */1738 if (pLogger->fFlags & (RTLOGFLAGS_PREFIX_MASK | RTLOGFLAGS_USECRLF))1739 {1740 RTLOGOUTPUTPREFIXEDARGS OutputArgs;1741 OutputArgs.pLogger = pLogger;1742 OutputArgs.iGroup = iGroup;1743 OutputArgs.fFlags = fFlags;1744 RTLogFormatV(rtLogOutputPrefixed, &OutputArgs, pszFormat, args);1745 }1746 else1747 RTLogFormatV(rtLogOutput, pLogger, pszFormat, args);1748 if ( !(pLogger->fFlags & RTLOGFLAGS_BUFFERED)1749 && pLogger->offScratch)1750 rtlogFlush(pLogger);1751 1752 /*1753 * Release the semaphore.1754 */1755 rtlogUnlock(pLogger);1756 }1757 1758 1759 /**1760 * printf like function for writing to the default log.1761 *1762 * @param pszFormat Printf like format string.1763 * @param ... Optional arguments as specified in pszFormat.1764 *1765 * @remark The API doesn't support formatting of floating point numbers at the moment.1766 */1767 RTDECL(void) RTLogPrintf(const char *pszFormat, ...)1768 {1769 va_list args;1770 va_start(args, pszFormat);1771 RTLogPrintfV(pszFormat, args);1772 va_end(args);1773 }1774 1775 1776 /**1777 * vprintf like function for writing to the default log.1778 *1779 * @param pszFormat Printf like format string.1780 * @param args Optional arguments as specified in pszFormat.1781 *1782 * @remark The API doesn't support formatting of floating point numbers at the moment.1783 */1784 RTDECL(void) RTLogPrintfV(const char *pszFormat, va_list args)1785 {1786 RTLogLoggerV(NULL, pszFormat, args);1787 }1788 1789 1790 /**1791 63 * printf like function for writing to the default release log. 1792 64 * … … 1804 76 } 1805 77 1806 1807 /**1808 * vprintf like function for writing to the default release log.1809 *1810 * @param pszFormat Printf like format string.1811 * @param args Optional arguments as specified in pszFormat.1812 *1813 * @remark The API doesn't support formatting of floating point numbers at the moment.1814 */1815 RTDECL(void) RTLogRelPrintfV(const char *pszFormat, va_list args)1816 {1817 RTLogRelLoggerV(NULL, 0, ~0U, pszFormat, args);1818 }1819 1820 1821 /**1822 * Writes the buffer to the given log device without checking for buffered1823 * data or anything.1824 * Used by the RTLogFlush() function.1825 *1826 * @param pLogger The logger instance to write to. NULL is not allowed!1827 */1828 static void rtlogFlush(PRTLOGGER pLogger)1829 {1830 #ifndef IN_GC1831 if (pLogger->fDestFlags & RTLOGDEST_USER)1832 RTLogWriteUser(pLogger->achScratch, pLogger->offScratch);1833 1834 if (pLogger->fDestFlags & RTLOGDEST_DEBUGGER)1835 RTLogWriteDebugger(pLogger->achScratch, pLogger->offScratch);1836 1837 # ifdef IN_RING31838 if (pLogger->fDestFlags & RTLOGDEST_FILE)1839 RTFileWrite(pLogger->File, pLogger->achScratch, pLogger->offScratch, NULL);1840 # endif1841 1842 if (pLogger->fDestFlags & RTLOGDEST_STDOUT)1843 RTLogWriteStdOut(pLogger->achScratch, pLogger->offScratch);1844 1845 if (pLogger->fDestFlags & RTLOGDEST_STDERR)1846 RTLogWriteStdErr(pLogger->achScratch, pLogger->offScratch);1847 1848 # if (defined(IN_RING0) || defined(IN_GC)) && !defined(LOG_NO_COM)1849 if (pLogger->fDestFlags & RTLOGDEST_COM)1850 RTLogWriteCom(pLogger->achScratch, pLogger->offScratch);1851 # endif1852 #endif /* !IN_GC */1853 1854 if (pLogger->pfnFlush)1855 pLogger->pfnFlush(pLogger);1856 1857 /* empty the buffer. */1858 pLogger->offScratch = 0;1859 }1860 1861 1862 /**1863 * Callback for RTLogFormatV which writes to the com port.1864 * See PFNLOGOUTPUT() for details.1865 */1866 static DECLCALLBACK(size_t) rtLogOutput(void *pv, const char *pachChars, size_t cbChars)1867 {1868 PRTLOGGER pLogger = (PRTLOGGER)pv;1869 if (cbChars)1870 {1871 size_t cbRet = 0;1872 for (;;)1873 {1874 #if defined(DEBUG) && defined(IN_RING3)1875 /* sanity */1876 if (pLogger->offScratch >= sizeof(pLogger->achScratch))1877 {1878 fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",1879 pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));1880 AssertBreakpoint(); AssertBreakpoint();1881 }1882 #endif1883 1884 /* how much */1885 size_t cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;1886 if (cb > cbChars)1887 cb = cbChars;1888 1889 /* copy */1890 memcpy(&pLogger->achScratch[pLogger->offScratch], pachChars, cb);1891 1892 /* advance */1893 pLogger->offScratch += cb;1894 cbRet += cb;1895 cbChars -= cb;1896 1897 /* done? */1898 if (cbChars <= 0)1899 return cbRet;1900 1901 pachChars += cb;1902 1903 /* flush */1904 rtlogFlush(pLogger);1905 }1906 1907 /* won't ever get here! */1908 }1909 else1910 {1911 /*1912 * Termination call.1913 * There's always space for a terminator, and it's not counted.1914 */1915 pLogger->achScratch[pLogger->offScratch] = '\0';1916 return 0;1917 }1918 }1919 1920 1921 1922 /**1923 * Callback for RTLogFormatV which writes to the logger instance.1924 * This version supports prefixes.1925 *1926 * See PFNLOGOUTPUT() for details.1927 */1928 static DECLCALLBACK(size_t) rtLogOutputPrefixed(void *pv, const char *pachChars, size_t cbChars)1929 {1930 PRTLOGOUTPUTPREFIXEDARGS pArgs = (PRTLOGOUTPUTPREFIXEDARGS)pv;1931 PRTLOGGER pLogger = pArgs->pLogger;1932 if (cbChars)1933 {1934 size_t cbRet = 0;1935 for (;;)1936 {1937 size_t cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;1938 char *psz;1939 const char *pszNewLine;1940 1941 /*1942 * Pending prefix?1943 */1944 if (pLogger->fPendingPrefix)1945 {1946 pLogger->fPendingPrefix = false;1947 1948 #if defined(DEBUG) && defined(IN_RING3)1949 /* sanity */1950 if (pLogger->offScratch >= sizeof(pLogger->achScratch))1951 {1952 fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",1953 pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));1954 AssertBreakpoint(); AssertBreakpoint();1955 }1956 #endif1957 1958 /*1959 * Flush the buffer if there isn't enough room for the maximum prefix config.1960 * Max is 224, add a couple of extra bytes.1961 */1962 if (cb < 224 + 16)1963 {1964 rtlogFlush(pLogger);1965 cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;1966 }1967 1968 /*1969 * Write the prefixes.1970 * psz is pointing to the current position.1971 */1972 psz = &pLogger->achScratch[pLogger->offScratch];1973 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TS)1974 {1975 #if defined(IN_RING3) || defined(IN_GC)1976 uint64_t u64 = RTTimeNanoTS();1977 #else1978 uint64_t u64 = ~0;1979 #endif1980 int iBase = 16;1981 unsigned int fFlags = RTSTR_F_ZEROPAD;1982 if (pLogger->fFlags & RTLOGFLAGS_DECIMAL_TS)1983 {1984 iBase = 10;1985 fFlags = 0;1986 }1987 if (pLogger->fFlags & RTLOGFLAGS_REL_TS)1988 {1989 static volatile uint64_t s_u64LastTs;1990 uint64_t u64DiffTs = u64 - s_u64LastTs;1991 s_u64LastTs = u64;1992 /* We could have been preempted just before reading of s_u64LastTs by1993 * another thread which wrote s_u64LastTs. In that case the difference1994 * is negative which we simply ignore. */1995 u64 = (int64_t)u64DiffTs < 0 ? 0 : u64DiffTs;1996 }1997 /* 1E15 nanoseconds = 11 days */1998 psz += RTStrFormatNumber(psz, u64, iBase, 16, 0, fFlags); /* +17 */1999 *psz++ = ' ';2000 }2001 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TSC)2002 {2003 uint64_t u64 = ASMReadTSC();2004 int iBase = 16;2005 unsigned int fFlags = RTSTR_F_ZEROPAD;2006 if (pLogger->fFlags & RTLOGFLAGS_DECIMAL_TS)2007 {2008 iBase = 10;2009 fFlags = 0;2010 }2011 if (pLogger->fFlags & RTLOGFLAGS_REL_TS)2012 {2013 static volatile uint64_t s_u64LastTsc;2014 uint64_t u64DiffTsc = u64 - s_u64LastTsc;2015 s_u64LastTsc = u64;2016 /* We could have been preempted just before reading of s_u64LastTsc by2017 * another thread which wrote s_u64LastTsc. In that case the difference2018 * is negative which we simply ignore. */2019 u64 = u64DiffTsc < 0 ? 0 : u64DiffTsc;2020 }2021 /* 1E15 ticks at 4GHz = 69 hours */2022 psz += RTStrFormatNumber(psz, u64, iBase, 16, 0, fFlags); /* +17 */2023 *psz++ = ' ';2024 }2025 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_MS_PROG)2026 {2027 #if defined(IN_RING3) || defined(IN_GC)2028 uint64_t u64 = RTTimeProgramMilliTS();2029 #else2030 uint64_t u64 = 0;2031 #endif2032 /* 1E8 milliseconds = 27 hours */2033 psz += RTStrFormatNumber(psz, u64, 10, 9, 0, RTSTR_F_ZEROPAD);2034 *psz++ = ' ';2035 }2036 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TIME)2037 {2038 #ifdef IN_RING32039 RTTIMESPEC TimeSpec;2040 RTTIME Time;2041 RTTimeExplode(&Time, RTTimeNow(&TimeSpec));2042 psz += RTStrFormatNumber(psz, Time.u8Hour, 10, 2, 0, RTSTR_F_ZEROPAD);2043 *psz++ = ':';2044 psz += RTStrFormatNumber(psz, Time.u8Minute, 10, 2, 0, RTSTR_F_ZEROPAD);2045 *psz++ = ':';2046 psz += RTStrFormatNumber(psz, Time.u8Second, 10, 2, 0, RTSTR_F_ZEROPAD);2047 *psz++ = '.';2048 psz += RTStrFormatNumber(psz, Time.u32Nanosecond / 1000000, 10, 3, 0, RTSTR_F_ZEROPAD);2049 *psz++ = ' '; /* +17 (3+1+3+1+3+1+4+1) */2050 #else2051 memset(psz, ' ', 13);2052 psz += 13;2053 #endif2054 }2055 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TIME_PROG)2056 {2057 #ifdef IN_RING32058 uint64_t u64 = RTTimeProgramMilliTS();2059 psz += RTStrFormatNumber(psz, (uint32_t)(u64 / (60 * 60 * 1000)), 10, 2, 0, RTSTR_F_ZEROPAD);2060 *psz++ = ':';2061 uint32_t u32 = (uint32_t)(u64 % (60 * 60 * 1000));2062 psz += RTStrFormatNumber(psz, u32 / (60 * 1000), 10, 2, 0, RTSTR_F_ZEROPAD);2063 *psz++ = ':';2064 u32 %= 60 * 1000;2065 psz += RTStrFormatNumber(psz, u32 / 1000, 10, 2, 0, RTSTR_F_ZEROPAD);2066 *psz++ = '.';2067 psz += RTStrFormatNumber(psz, u32 % 1000, 10, 3, 0, RTSTR_F_ZEROPAD);2068 *psz++ = ' '; /* +20 (9+1+2+1+2+1+3+1) */2069 #else2070 memset(psz, ' ', 13);2071 psz += 13;2072 #endif2073 }2074 # if 02075 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_DATETIME)2076 {2077 char szDate[32];2078 RTTIMESPEC Time;2079 RTTimeSpecToString(RTTimeNow(&Time), szDate, sizeof(szDate));2080 size_t cch = strlen(szDate);2081 memcpy(psz, szDate, cch);2082 psz += cch;2083 *psz++ = ' '; /* +32 */2084 }2085 # endif2086 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_PID)2087 {2088 #ifndef IN_GC2089 RTPROCESS Process = RTProcSelf();2090 #else2091 RTPROCESS Process = NIL_RTPROCESS;2092 #endif2093 psz += RTStrFormatNumber(psz, Process, 16, sizeof(RTPROCESS) * 2, 0, RTSTR_F_ZEROPAD);2094 *psz++ = ' '; /* +9 */2095 }2096 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_TID)2097 {2098 #ifndef IN_GC2099 RTNATIVETHREAD Thread = RTThreadNativeSelf();2100 #else2101 RTNATIVETHREAD Thread = NIL_RTNATIVETHREAD;2102 #endif2103 psz += RTStrFormatNumber(psz, Thread, 16, sizeof(RTNATIVETHREAD) * 2, 0, RTSTR_F_ZEROPAD);2104 *psz++ = ' '; /* +17 */2105 }2106 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_THREAD)2107 {2108 #ifdef IN_RING32109 const char *pszName = RTThreadSelfName();2110 #elif defined IN_GC2111 const char *pszName = "EMT-GC";2112 #else2113 const char *pszName = "EMT-R0";2114 #endif2115 size_t cch = 0;2116 if (pszName)2117 {2118 cch = strlen(pszName);2119 cch = RT_MIN(cch, 16);2120 memcpy(psz, pszName, cch);2121 psz += cch;2122 }2123 do2124 *psz++ = ' ';2125 while (cch++ < 8); /* +17 */2126 }2127 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_CPUID)2128 {2129 #if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)2130 const uint8_t idCpu = ASMGetApicId();2131 #else2132 const RTCPUID idCpu = RTMpCpuId();2133 #endif2134 psz += RTStrFormatNumber(psz, idCpu, 16, sizeof(idCpu) * 2, 0, RTSTR_F_ZEROPAD);2135 *psz++ = ' '; /* +17 */2136 }2137 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_LOCK_COUNTS)2138 {2139 #ifdef IN_RING3 /** @todo implement these counters in ring-0 too? */2140 RTTHREAD Thread = RTThreadSelf();2141 if (Thread != NIL_RTTHREAD)2142 {2143 uint32_t cReadLocks = RTThreadGetReadLockCount(Thread);2144 uint32_t cWriteLocks = RTThreadGetWriteLockCount(Thread) - g_cLoggerLockCount;2145 cReadLocks = RT_MIN(0xfff, cReadLocks);2146 cWriteLocks = RT_MIN(0xfff, cWriteLocks);2147 psz += RTStrFormatNumber(psz, cReadLocks, 16, 1, 0, RTSTR_F_ZEROPAD);2148 *psz++ = '/';2149 psz += RTStrFormatNumber(psz, cWriteLocks, 16, 1, 0, RTSTR_F_ZEROPAD);2150 }2151 else2152 #endif2153 {2154 *psz++ = '?';2155 *psz++ = '/';2156 *psz++ = '?';2157 }2158 *psz++ = ' '; /* +8 */2159 }2160 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_FLAG_NO)2161 {2162 psz += RTStrFormatNumber(psz, pArgs->fFlags, 16, 8, 0, RTSTR_F_ZEROPAD);2163 *psz++ = ' '; /* +9 */2164 }2165 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_FLAG)2166 {2167 #ifdef IN_RING32168 const char *pszGroup = pArgs->iGroup != ~0U ? pLogger->papszGroups[pArgs->iGroup] : NULL;2169 #else2170 const char *pszGroup = NULL;2171 #endif2172 size_t cch = 0;2173 if (pszGroup)2174 {2175 cch = strlen(pszGroup);2176 cch = RT_MIN(cch, 16);2177 memcpy(psz, pszGroup, cch);2178 psz += cch;2179 }2180 do2181 *psz++ = ' ';2182 while (cch++ < 8); /* +17 */2183 }2184 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_GROUP_NO)2185 {2186 if (pArgs->iGroup != ~0U)2187 {2188 psz += RTStrFormatNumber(psz, pArgs->iGroup, 16, 3, 0, RTSTR_F_ZEROPAD);2189 *psz++ = ' ';2190 }2191 else2192 {2193 memcpy(psz, "-1 ", sizeof("-1 ") - 1);2194 psz += sizeof("-1 ") - 1;2195 } /* +9 */2196 }2197 if (pLogger->fFlags & RTLOGFLAGS_PREFIX_GROUP)2198 {2199 const unsigned fGrp = pLogger->afGroups[pArgs->iGroup != ~0U ? pArgs->iGroup : 0];2200 const char *pszGroup;2201 size_t cch;2202 switch (pArgs->fFlags & fGrp)2203 {2204 case 0: pszGroup = "--------"; cch = sizeof("--------") - 1; break;2205 case RTLOGGRPFLAGS_ENABLED: pszGroup = "enabled" ; cch = sizeof("enabled" ) - 1; break;2206 case RTLOGGRPFLAGS_LEVEL_1: pszGroup = "level 1" ; cch = sizeof("level 1" ) - 1; break;2207 case RTLOGGRPFLAGS_LEVEL_2: pszGroup = "level 2" ; cch = sizeof("level 2" ) - 1; break;2208 case RTLOGGRPFLAGS_LEVEL_3: pszGroup = "level 3" ; cch = sizeof("level 3" ) - 1; break;2209 case RTLOGGRPFLAGS_LEVEL_4: pszGroup = "level 4" ; cch = sizeof("level 4" ) - 1; break;2210 case RTLOGGRPFLAGS_LEVEL_5: pszGroup = "level 5" ; cch = sizeof("level 5" ) - 1; break;2211 case RTLOGGRPFLAGS_LEVEL_6: pszGroup = "level 6" ; cch = sizeof("level 6" ) - 1; break;2212 case RTLOGGRPFLAGS_FLOW: pszGroup = "flow" ; cch = sizeof("flow" ) - 1; break;2213 2214 /* personal groups */2215 case RTLOGGRPFLAGS_LELIK: pszGroup = "lelik" ; cch = sizeof("lelik" ) - 1; break;2216 case RTLOGGRPFLAGS_MICHAEL: pszGroup = "Michael" ; cch = sizeof("Michael" ) - 1; break;2217 case RTLOGGRPFLAGS_DMIK: pszGroup = "dmik" ; cch = sizeof("dmik" ) - 1; break;2218 case RTLOGGRPFLAGS_SUNLOVER: pszGroup = "sunlover"; cch = sizeof("sunlover") - 1; break;2219 case RTLOGGRPFLAGS_ACHIM: pszGroup = "Achim" ; cch = sizeof("Achim" ) - 1; break;2220 case RTLOGGRPFLAGS_SANDER: pszGroup = "Sander" ; cch = sizeof("Sander" ) - 1; break;2221 case RTLOGGRPFLAGS_KLAUS: pszGroup = "Klaus" ; cch = sizeof("Klaus" ) - 1; break;2222 case RTLOGGRPFLAGS_FRANK: pszGroup = "Frank" ; cch = sizeof("Frank" ) - 1; break;2223 case RTLOGGRPFLAGS_BIRD: pszGroup = "bird" ; cch = sizeof("bird" ) - 1; break;2224 case RTLOGGRPFLAGS_NONAME: pszGroup = "noname" ; cch = sizeof("noname" ) - 1; break;2225 default: pszGroup = "????????"; cch = sizeof("????????") - 1; break;2226 }2227 if (pszGroup)2228 {2229 cch = RT_MIN(cch, 16);2230 memcpy(psz, pszGroup, cch);2231 psz += cch;2232 }2233 do2234 *psz++ = ' ';2235 while (cch++ < 8); /* +17 */2236 }2237 2238 /*2239 * Done, figure what we've used and advance the buffer and free size.2240 */2241 cb = psz - &pLogger->achScratch[pLogger->offScratch];2242 Assert(cb <= 198);2243 pLogger->offScratch += cb;2244 cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;2245 }2246 else if (cb <= 0)2247 {2248 rtlogFlush(pLogger);2249 cb = sizeof(pLogger->achScratch) - pLogger->offScratch - 1;2250 }2251 2252 #if defined(DEBUG) && defined(IN_RING3)2253 /* sanity */2254 if (pLogger->offScratch >= sizeof(pLogger->achScratch))2255 {2256 fprintf(stderr, "pLogger->offScratch >= sizeof(pLogger->achScratch) (%#x >= %#x)\n",2257 pLogger->offScratch, (unsigned)sizeof(pLogger->achScratch));2258 AssertBreakpoint(); AssertBreakpoint();2259 }2260 #endif2261 2262 /* how much */2263 if (cb > cbChars)2264 cb = cbChars;2265 2266 /* have newline? */2267 pszNewLine = (const char *)memchr(pachChars, '\n', cb);2268 if (pszNewLine)2269 {2270 if (pLogger->fFlags & RTLOGFLAGS_USECRLF)2271 cb = pszNewLine - pachChars;2272 else2273 {2274 cb = pszNewLine - pachChars + 1;2275 pLogger->fPendingPrefix = true;2276 }2277 }2278 2279 /* copy */2280 memcpy(&pLogger->achScratch[pLogger->offScratch], pachChars, cb);2281 2282 /* advance */2283 pLogger->offScratch += cb;2284 cbRet += cb;2285 cbChars -= cb;2286 2287 if ( pszNewLine2288 && (pLogger->fFlags & RTLOGFLAGS_USECRLF)2289 && pLogger->offScratch + 2 < sizeof(pLogger->achScratch))2290 {2291 memcpy(&pLogger->achScratch[pLogger->offScratch], "\r\n", 2);2292 pLogger->offScratch += 2;2293 cbRet++;2294 cbChars--;2295 cb++;2296 pLogger->fPendingPrefix = true;2297 }2298 2299 /* done? */2300 if (cbChars <= 0)2301 return cbRet;2302 pachChars += cb;2303 }2304 2305 /* won't ever get here! */2306 }2307 else2308 {2309 /*2310 * Termination call.2311 * There's always space for a terminator, and it's not counted.2312 */2313 pLogger->achScratch[pLogger->offScratch] = '\0';2314 return 0;2315 }2316 }2317
Note:
See TracChangeset
for help on using the changeset viewer.