/* $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 . * * 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 #include #include #include #include #include /* * 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 /********************************************************************************************************************************* * 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> \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; }