diff options
Diffstat (limited to 'src/VBox/Runtime/common/err/errmsg-sorter.cpp')
-rw-r--r-- | src/VBox/Runtime/common/err/errmsg-sorter.cpp | 483 |
1 files changed, 483 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/err/errmsg-sorter.cpp b/src/VBox/Runtime/common/err/errmsg-sorter.cpp new file mode 100644 index 00000000..a4c68865 --- /dev/null +++ b/src/VBox/Runtime/common/err/errmsg-sorter.cpp @@ -0,0 +1,483 @@ +/* $Id: errmsg-sorter.cpp $ */ +/** @file + * IPRT - Status code messages, sorter build program. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/err.h> +#include <iprt/asm.h> +#include <iprt/string.h> +#include <VBox/err.h> + +#include <stdio.h> +#include <stdlib.h> + + +/* + * Include the string table code. + */ +#define BLDPROG_STRTAB_MAX_STRLEN 512 +#define BLDPROG_STRTAB_WITH_COMPRESSION +#define BLDPROG_STRTAB_PURE_ASCII +#define BLDPROG_STRTAB_WITH_CAMEL_WORDS +#include <iprt/bldprog-strtab-template.cpp.h> + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** Used for raw-input and sorting. */ +typedef struct RTSTATUSMSGINT1 +{ + /** Pointer to the short message string. */ + const char *pszMsgShort; + /** Pointer to the full message string. */ + const char *pszMsgFull; + /** Pointer to the define string. */ + const char *pszDefine; + /** Status code number. */ + int iCode; + /** Set if duplicate. */ + bool fDuplicate; +} RTSTATUSMSGINT1; +typedef RTSTATUSMSGINT1 *PRTSTATUSMSGINT1; + + +/** This is used when building the string table and printing it. */ +typedef struct RTSTATUSMSGINT2 +{ + /** The short message string. */ + BLDPROGSTRING MsgShort; + /** The full message string. */ + BLDPROGSTRING MsgFull; + /** The define string. */ + BLDPROGSTRING Define; + /** Pointer to the define string. */ + const char *pszDefine; + /** Status code number. */ + int iCode; + /** Index into the primary table (for multiple passes). */ + unsigned idx1; +} RTSTATUSMSGINT2; +typedef RTSTATUSMSGINT2 *PRTSTATUSMSGINT2; + + +/** This used to determin minimum field sizes. */ +typedef struct RTSTATUSMSGSTATS +{ + unsigned offMax; + unsigned cchMax; + unsigned cBitsOffset; + unsigned cBitsLength; +} RTSTATUSMSGSTATS; +typedef RTSTATUSMSGSTATS *PRTSTATUSMSGSTATS; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +static const char *g_pszProgName = "errmsg-sorter"; +static RTSTATUSMSGINT1 g_aStatusMsgs[] = +{ +#if !defined(IPRT_NO_ERROR_DATA) && !defined(DOXYGEN_RUNNING) +# include "errmsgdata.h" +#else + { "Success.", "Success.", "VINF_SUCCESS", 0, false }, +#endif +}; + + +static RTEXITCODE error(const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + fprintf(stderr, "%s: error: ", g_pszProgName); + vfprintf(stderr, pszFormat, va); + va_end(va); + return RTEXITCODE_FAILURE; +} + + +/** qsort callback. */ +static int CompareErrMsg(const void *pv1, const void *pv2) RT_NOTHROW_DEF +{ + PRTSTATUSMSGINT1 p1 = (PRTSTATUSMSGINT1)pv1; + PRTSTATUSMSGINT1 p2 = (PRTSTATUSMSGINT1)pv2; + int iDiff; + if (p1->iCode < p2->iCode) + iDiff = -1; + else if (p1->iCode > p2->iCode) + iDiff = 1; + else + iDiff = 0; + return iDiff; +} + + +/** + * Checks whether @a pszDefine is a deliberate duplicate define that should be + * omitted. + */ +static bool IgnoreDuplicateDefine(const char *pszDefine) +{ + size_t const cchDefine = strlen(pszDefine); + + static const RTSTRTUPLE s_aTails[] = + { + { RT_STR_TUPLE("_FIRST") }, + { RT_STR_TUPLE("_LAST") }, + { RT_STR_TUPLE("_HIGEST") }, + { RT_STR_TUPLE("_LOWEST") }, + }; + for (size_t i = 0; i < RT_ELEMENTS(s_aTails); i++) + if ( cchDefine > s_aTails[i].cch + && memcmp(&pszDefine[cchDefine - s_aTails[i].cch], s_aTails[i].psz, s_aTails[i].cch) == 0) + return true; + + static const RTSTRTUPLE s_aDeliberateOrSilly[] = + { + { RT_STR_TUPLE("VERR_VRDP_TIMEOUT") }, + { RT_STR_TUPLE("VINF_VRDP_SUCCESS") }, + { RT_STR_TUPLE("VWRN_CONTINUE_RECOMPILE") }, + { RT_STR_TUPLE("VWRN_PATM_CONTINUE_SEARCH") }, + }; + for (size_t i = 0; i < RT_ELEMENTS(s_aDeliberateOrSilly); i++) + if ( cchDefine == s_aDeliberateOrSilly[i].cch + && memcmp(pszDefine, s_aDeliberateOrSilly[i].psz, cchDefine) == 0) + return true; + + return false; +} + + +DECLINLINE(void) GatherStringStats(PRTSTATUSMSGSTATS pStats, PBLDPROGSTRING pString) +{ + if (pStats->offMax < pString->offStrTab) + pStats->offMax = pString->offStrTab; + if (pStats->cchMax < pString->cchString) + pStats->cchMax = (unsigned)pString->cchString; +} + + +DECLINLINE(unsigned) CalcBitsForValue(size_t uValue) +{ + unsigned cBits = 1; + while (RT_BIT_64(cBits) < uValue && cBits < 64) + cBits++; + return cBits; +} + + +static void CalcBitsForStringStats(PRTSTATUSMSGSTATS pStats) +{ + pStats->cBitsOffset = CalcBitsForValue(pStats->offMax); + pStats->cBitsLength = CalcBitsForValue(pStats->cchMax); +} + + +int main(int argc, char **argv) +{ + /* + * Parse arguments. + */ + enum { kMode_All, kMode_NoFullMsg, kMode_OnlyDefines } enmMode; + if (argc == 3 && strcmp(argv[1], "--all") == 0) + enmMode = kMode_All; + else if (argc == 3 && strcmp(argv[1], "--no-full-msg") == 0) + enmMode = kMode_NoFullMsg; + else if (argc == 3 && strcmp(argv[1], "--only-defines") == 0) + enmMode = kMode_OnlyDefines; + else + { + fprintf(stderr, + "syntax error!\n" + "Usage: %s <--all|--no-full-msg|--only-defines> <outfile>\n", argv[0]); + return RTEXITCODE_SYNTAX; + } + const char * const pszOutFile = argv[2]; + + /* + * Sort the table and mark duplicates. + */ + qsort(g_aStatusMsgs, RT_ELEMENTS(g_aStatusMsgs), sizeof(g_aStatusMsgs[0]), CompareErrMsg); + + int rcExit = RTEXITCODE_SUCCESS; + int iPrev = INT32_MAX; + for (size_t i = 0; i < RT_ELEMENTS(g_aStatusMsgs); i++) + { + /* Deal with duplicates, trying to eliminate unnecessary *_FIRST, *_LAST, + *_LOWEST, and *_HIGHEST entries as well as some deliberate duplicate entries. + This means we need to look forward and backwards here. */ + PRTSTATUSMSGINT1 pMsg = &g_aStatusMsgs[i]; + if (pMsg->iCode == iPrev && i != 0) + { + if (IgnoreDuplicateDefine(pMsg->pszDefine)) + { + pMsg->fDuplicate = true; + continue; + } + PRTSTATUSMSGINT1 pPrev = &g_aStatusMsgs[i - 1]; + rcExit = error("Duplicate value %d - %s and %s\n", iPrev, pMsg->pszDefine, pPrev->pszDefine); + } + else if (i + 1 < RT_ELEMENTS(g_aStatusMsgs)) + { + PRTSTATUSMSGINT1 pNext = &g_aStatusMsgs[i]; + if ( pMsg->iCode == pNext->iCode + && IgnoreDuplicateDefine(pMsg->pszDefine)) + { + pMsg->fDuplicate = true; + continue; + } + } + iPrev = pMsg->iCode; + pMsg->fDuplicate = false; + } + + /* + * Create a string table for it all. + */ + BLDPROGSTRTAB StrTab; + if (!BldProgStrTab_Init(&StrTab, RT_ELEMENTS(g_aStatusMsgs) * 3)) + return error("Out of memory!\n"); + + static RTSTATUSMSGINT2 s_aStatusMsgs2[RT_ELEMENTS(g_aStatusMsgs)]; + unsigned cStatusMsgs = 0; + for (unsigned i = 0; i < RT_ELEMENTS(g_aStatusMsgs); i++) + if (!g_aStatusMsgs[i].fDuplicate) + { + s_aStatusMsgs2[cStatusMsgs].idx1 = i; + s_aStatusMsgs2[cStatusMsgs].iCode = g_aStatusMsgs[i].iCode; + s_aStatusMsgs2[cStatusMsgs].pszDefine = g_aStatusMsgs[i].pszDefine; + BldProgStrTab_AddStringDup(&StrTab, &s_aStatusMsgs2[cStatusMsgs].Define, g_aStatusMsgs[i].pszDefine); + cStatusMsgs++; + } + + if (enmMode != kMode_OnlyDefines) + for (size_t i = 0; i < cStatusMsgs; i++) + BldProgStrTab_AddStringDup(&StrTab, &s_aStatusMsgs2[i].MsgShort, g_aStatusMsgs[s_aStatusMsgs2[i].idx1].pszMsgShort); + + if (enmMode == kMode_All) + for (size_t i = 0; i < cStatusMsgs; i++) + BldProgStrTab_AddStringDup(&StrTab, &s_aStatusMsgs2[i].MsgFull, g_aStatusMsgs[s_aStatusMsgs2[i].idx1].pszMsgFull); + + if (!BldProgStrTab_CompileIt(&StrTab, true)) + return error("BldProgStrTab_CompileIt failed!\n"); + + /* + * Prepare output file. + */ + FILE *pOut = fopen(pszOutFile, "wt"); + if (pOut) + { + /* + * . + */ + RTSTATUSMSGSTATS Defines = {0, 0, 0, 0}; + RTSTATUSMSGSTATS MsgShort = {0, 0, 0, 0}; + RTSTATUSMSGSTATS MsgFull = {0, 0, 0, 0}; + for (size_t i = 0; i < cStatusMsgs; i++) + { + GatherStringStats(&Defines, &s_aStatusMsgs2[i].Define); + GatherStringStats(&MsgShort, &s_aStatusMsgs2[i].MsgShort); + GatherStringStats(&MsgFull, &s_aStatusMsgs2[i].MsgFull); + } + CalcBitsForStringStats(&Defines); + CalcBitsForStringStats(&MsgShort); + CalcBitsForStringStats(&MsgFull); + printf(" Defines: max offset %#x -> %u bits, max length %#x -> bits %u\n", + Defines.offMax, Defines.cBitsOffset, (unsigned)Defines.cchMax, Defines.cBitsLength); + if (enmMode != kMode_OnlyDefines) + printf("MsgShort: max offset %#x -> %u bits, max length %#x -> bits %u\n", + MsgShort.offMax, MsgShort.cBitsOffset, (unsigned)MsgShort.cchMax, MsgShort.cBitsLength); + if (enmMode == kMode_All) + printf(" MsgFull: max offset %#x -> %u bits, max length %#x -> bits %u\n", + MsgFull.offMax, MsgFull.cBitsOffset, (unsigned)MsgFull.cchMax, MsgFull.cBitsLength); + + unsigned cBitsCodePos = CalcBitsForValue((size_t)s_aStatusMsgs2[cStatusMsgs - 1].iCode); + unsigned cBitsCodeNeg = CalcBitsForValue((size_t)-s_aStatusMsgs2[0].iCode); + unsigned cBitsCode = RT_MAX(cBitsCodePos, cBitsCodeNeg) + 1; + printf("Statuses: min %d, max %d -> %u bits\n", + s_aStatusMsgs2[0].iCode, s_aStatusMsgs2[cStatusMsgs - 1].iCode, cBitsCode); + + /* + * Print the table. + */ + fprintf(pOut, + "\n" + "#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)\n" + "# pragma pack(1)\n" + "#endif\n" + "typedef struct RTMSGENTRYINT\n" + "{\n"); + /* 16 + 16 + 8 */ + bool fOptimalLayout = true; + if ( enmMode == kMode_OnlyDefines + && cBitsCode <= 16 + && Defines.cBitsOffset <= 16 + && Defines.cBitsLength <= 8) + fprintf(pOut, + " uint16_t offDefine; /* need %2u bits, max %#x */\n" + " uint8_t cchDefine; /* need %2u bits, max %#x */\n" + " int16_t iCode; /* need %2u bits */\n", + Defines.cBitsOffset, Defines.offMax, Defines.cBitsLength, Defines.cchMax, cBitsCode); + else if ( enmMode == kMode_NoFullMsg + && cBitsCode + Defines.cBitsOffset + Defines.cBitsLength + MsgShort.cBitsOffset + MsgShort.cBitsLength <= 64) + fprintf(pOut, + " uint64_t offDefine : %2u; /* max %#x */\n" + " uint64_t cchDefine : %2u; /* max %#x */\n" + " uint64_t offMsgShort : %2u; /* max %#x */\n" + " uint64_t cchMsgShort : %2u; /* max %#x */\n" + " int64_t iCode : %2u;\n", + Defines.cBitsOffset, Defines.offMax, + Defines.cBitsLength, Defines.cchMax, + MsgShort.cBitsOffset, MsgShort.offMax, + MsgShort.cBitsLength, MsgShort.cchMax, + cBitsCode); + else if ( enmMode == kMode_All + && Defines.cBitsOffset + Defines.cBitsLength + + MsgShort.cBitsOffset + MsgShort.cBitsLength + + MsgFull.cBitsOffset + MsgFull.cBitsLength + + cBitsCode <= 96 + && cBitsCode + Defines.cBitsLength + MsgShort.cBitsLength <= 32) + fprintf(pOut, + " uint64_t offDefine : %2u; /* max %#x */\n" + " uint64_t offMsgShort : %2u; /* max %#x */\n" + " uint64_t offMsgFull : %2u; /* max %#x */\n" + " uint64_t cchMsgFull : %2u; /* max %#x */\n" + " int32_t iCode : %2u;\n" + " uint32_t cchDefine : %2u; /* max %#x */\n" + " uint32_t cchMsgShort : %2u; /* max %#x */\n", + Defines.cBitsOffset, Defines.offMax, + MsgShort.cBitsOffset, MsgShort.offMax, + MsgFull.cBitsOffset, MsgFull.offMax, + MsgFull.cBitsLength, MsgFull.cchMax, + cBitsCode, + Defines.cBitsLength, Defines.cchMax, + MsgShort.cBitsLength, MsgShort.cchMax); + else + { + fprintf(stderr, "%s: warning: Optimized structure layouts needs readjusting...\n", g_pszProgName); + fOptimalLayout = false; + fprintf(pOut, + " uint32_t offDefine : 23; /* need %u bits, max %#x */\n" + " uint32_t cchDefine : 9; /* need %u bits, max %#x */\n", + Defines.cBitsOffset, Defines.offMax, Defines.cBitsLength, Defines.cchMax); + if (enmMode != kMode_OnlyDefines) + fprintf(pOut, + " uint32_t offMsgShort : 23; /* need %u bits, max %#x */\n" + " uint32_t cchMsgShort : 9; /* need %u bits, max %#x */\n", + MsgShort.cBitsOffset, MsgShort.offMax, MsgShort.cBitsLength, MsgShort.offMax); + if (enmMode == kMode_All) + fprintf(pOut, + " uint32_t offMsgFull : 23; /* need %u bits, max %#x */\n" + " uint32_t cchMsgFull : 9; /* need %u bits, max %#x */\n", + MsgFull.cBitsOffset, MsgFull.offMax, MsgFull.cBitsLength, MsgFull.cchMax); + fprintf(pOut, + " int32_t iCode; /* need %u bits */\n", cBitsCode); + } + fprintf(pOut, + "} RTMSGENTRYINT;\n" + "typedef RTMSGENTRYINT *PCRTMSGENTRYINT;\n" + "#if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)\n" + "# pragma pack()\n" + "#endif\n" + "\n" + "static const RTMSGENTRYINT g_aStatusMsgs[ /*%lu*/ ] =\n" + "{\n" + , + (unsigned long)cStatusMsgs); + + if (enmMode == kMode_All && fOptimalLayout) + for (size_t i = 0; i < cStatusMsgs; i++) + fprintf(pOut, " { %#08x, %#08x, %#08x, %3u, %6d, %3u, %3u }, /* %s */\n", + s_aStatusMsgs2[i].Define.offStrTab, + s_aStatusMsgs2[i].MsgShort.offStrTab, + s_aStatusMsgs2[i].MsgFull.offStrTab, + (unsigned)s_aStatusMsgs2[i].MsgFull.cchString, + s_aStatusMsgs2[i].iCode, + (unsigned)s_aStatusMsgs2[i].Define.cchString, + (unsigned)s_aStatusMsgs2[i].MsgShort.cchString, + s_aStatusMsgs2[i].pszDefine); + else if (enmMode == kMode_All) + for (size_t i = 0; i < cStatusMsgs; i++) + fprintf(pOut, " { %#08x, %3u, %#08x, %3u, %#08x, %3u, %8d }, /* %s */\n", + s_aStatusMsgs2[i].Define.offStrTab, + (unsigned)s_aStatusMsgs2[i].Define.cchString, + s_aStatusMsgs2[i].MsgShort.offStrTab, + (unsigned)s_aStatusMsgs2[i].MsgShort.cchString, + s_aStatusMsgs2[i].MsgFull.offStrTab, + (unsigned)s_aStatusMsgs2[i].MsgFull.cchString, + s_aStatusMsgs2[i].iCode, + s_aStatusMsgs2[i].pszDefine); + else if (enmMode == kMode_NoFullMsg) + for (size_t i = 0; i < cStatusMsgs; i++) + fprintf(pOut, " { %#08x, %3u, %#08x, %3u, %8d }, /* %s */\n", + s_aStatusMsgs2[i].Define.offStrTab, + (unsigned)s_aStatusMsgs2[i].Define.cchString, + s_aStatusMsgs2[i].MsgShort.offStrTab, + (unsigned)s_aStatusMsgs2[i].MsgShort.cchString, + s_aStatusMsgs2[i].iCode, + s_aStatusMsgs2[i].pszDefine); + else if (enmMode == kMode_OnlyDefines) + for (size_t i = 0; i < cStatusMsgs; i++) + fprintf(pOut, " { %#08x, %3u, %8d }, /* %s */\n", + s_aStatusMsgs2[i].Define.offStrTab, + (unsigned)s_aStatusMsgs2[i].Define.cchString, + s_aStatusMsgs2[i].iCode, + s_aStatusMsgs2[i].pszDefine); + else + return error("Unsupported message selection (%d)!\n", enmMode); + fprintf(pOut, + "};\n" + "\n"); + + BldProgStrTab_WriteStringTable(&StrTab, pOut, "static ", "g_", "StatusMsgStrTab"); + + /* + * Close the output file and we're done. + */ + fflush(pOut); + if (ferror(pOut)) + rcExit = error("Error writing '%s'!\n", pszOutFile); + if (fclose(pOut) != 0) + rcExit = error("Failed to close '%s' after writing it!\n", pszOutFile); + } + else + rcExit = error("Failed to open '%s' for writing!\n", pszOutFile); + return rcExit; +} + |