_ZN4ipmi8tryParseIhEEbNSt3__117basic_string_viewIcNS1_11char_traitsIcEEEERT_i:
  475|    113|{
  476|    113|    static_assert(std::is_integral_v<T>,
  477|    113|                  "tryParse only supports integral types");
  478|       |
  479|    113|    if (s.empty())
  ------------------
  |  Branch (479:9): [True: 0, False: 113]
  ------------------
  480|      0|    {
  481|      0|        return false;
  482|      0|    }
  483|       |
  484|    113|    if (base == 16 && s.size() >= 2 && s[0] == '0' &&
  ------------------
  |  Branch (484:9): [True: 0, False: 113]
  |  Branch (484:23): [True: 0, False: 0]
  |  Branch (484:40): [True: 0, False: 0]
  ------------------
  485|      0|        (s[1] == 'x' || s[1] == 'X'))
  ------------------
  |  Branch (485:10): [True: 0, False: 0]
  |  Branch (485:25): [True: 0, False: 0]
  ------------------
  486|      0|    {
  487|      0|        s.remove_prefix(2);
  488|      0|    }
  489|       |
  490|    113|    auto result = std::from_chars(s.data(), s.data() + s.size(), out, base);
  491|       |
  492|    113|    return result.ec == std::errc{} && result.ptr == s.data() + s.size();
  ------------------
  |  Branch (492:12): [True: 4, False: 109]
  |  Branch (492:40): [True: 1, False: 3]
  ------------------
  493|    113|}

_ZN4ipmi3fru19preFormatProcessingEbRNSt3__16vectorIhNS1_9allocatorIhEEEE:
   70|  1.04k|{
   71|       |    // Add id for version of FRU Info Storage Spec used
   72|  1.04k|    data.emplace_back(specVersion);
   73|       |
   74|       |    // Add Data Size - 0 as a placeholder, can edit after the data is finalized
   75|  1.04k|    data.emplace_back(typeLengthByteNull);
   76|       |
   77|  1.04k|    if (langCode)
  ------------------
  |  Branch (77:9): [True: 785, False: 257]
  ------------------
   78|    785|    {
   79|    785|        data.emplace_back(englishLanguageCode);
   80|    785|    }
   81|  1.04k|}
_ZN4ipmi3fru18appendDataChecksumERNSt3__16vectorIhNS1_9allocatorIhEEEE:
   89|  1.78k|{
   90|  1.78k|    uint8_t checksumVal = std::accumulate(data.begin(), data.end(), 0);
   91|       |    // Push the Zero checksum as the last byte of this data
   92|       |    // This appears to be a simple summation of all the bytes
   93|  1.78k|    data.emplace_back(-checksumVal);
   94|  1.78k|}
_ZN4ipmi3fru7padDataERNSt3__16vectorIhNS1_9allocatorIhEEEE:
  102|  1.04k|{
  103|  1.04k|    uint8_t pad = (data.size() + checksumSize) % recordUnitOfMeasurement;
  104|  1.04k|    if (pad)
  ------------------
  |  Branch (104:9): [True: 915, False: 127]
  ------------------
  105|    915|    {
  106|    915|        data.resize((data.size() + recordUnitOfMeasurement - pad));
  107|    915|    }
  108|  1.04k|}
_ZN4ipmi3fru20postFormatProcessingERNSt3__16vectorIhNS1_9allocatorIhEEEE:
  116|  1.04k|{
  117|       |    // This area needs to be padded to a multiple of 8 bytes (after checksum)
  118|  1.04k|    padData(data);
  119|       |
  120|       |    // Set size of data info area
  121|  1.04k|    data.at(areaSizeOffset) =
  122|  1.04k|        (data.size() + checksumSize) / (recordUnitOfMeasurement);
  123|       |
  124|       |    // Finally add area checksum
  125|  1.04k|    appendDataChecksum(data);
  126|  1.04k|}
_ZN4ipmi3fru17appendChassisTypeERKNSt3__13mapINS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES8_NS1_4lessIS8_EENS6_INS1_4pairIKS8_S8_EEEEEERNS1_6vectorIhNS6_IhEEEE:
  136|    257|{
  137|    257|    uint8_t chassisType = 0; // Not specified
  138|    257|    auto iter = propMap.find(type);
  139|    257|    if (iter != propMap.end())
  ------------------
  |  Branch (139:9): [True: 113, False: 144]
  ------------------
  140|    113|    {
  141|    113|        auto value = iter->second;
  142|    113|        if (!ipmi::tryParse(value, chassisType))
  ------------------
  |  Branch (142:13): [True: 112, False: 1]
  ------------------
  143|    112|        {
  144|    112|            lg2::error("Could not parse chassis type, value: {VALUE}", "VALUE",
  145|    112|                       value);
  146|    112|            chassisType = 0;
  147|    112|        }
  148|    113|    }
  149|    257|    data.emplace_back(chassisType);
  150|    257|}
_ZN4ipmi3fru10appendDataERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEERKNS1_3mapIS7_S7_NS1_4lessIS7_EENS5_INS1_4pairIS8_S7_EEEEEERNS1_6vectorIhNS5_IhEEEE:
  161|  3.99k|{
  162|  3.99k|    auto iter = propMap.find(key);
  163|  3.99k|    if (iter != propMap.end())
  ------------------
  |  Branch (163:9): [True: 1.81k, False: 2.17k]
  ------------------
  164|  1.81k|    {
  165|  1.81k|        auto value = iter->second;
  166|       |        // If starts with 0x or 0X remove them
  167|       |        // ex: 0x123a just take 123a
  168|  1.81k|        if ((value.compare(0, 2, "0x")) == 0 ||
  ------------------
  |  Branch (168:13): [True: 45, False: 1.77k]
  ------------------
  169|  1.77k|            (value.compare(0, 2, "0X") == 0))
  ------------------
  |  Branch (169:13): [True: 62, False: 1.70k]
  ------------------
  170|    107|        {
  171|    107|            value.erase(0, 2);
  172|    107|        }
  173|       |
  174|       |        // 6 bits for length as per FRU spec v1.0
  175|       |        // if length is greater then 63(2^6) bytes then trim the data to 63
  176|       |        // bytess.
  177|  1.81k|        auto valueLength = (value.length() > maxRecordAttributeValue)
  ------------------
  |  Branch (177:28): [True: 0, False: 1.81k]
  ------------------
  178|  1.81k|                               ? maxRecordAttributeValue
  179|  1.81k|                               : value.length();
  180|       |        // 2 bits for type
  181|       |        // Set the type to ascii
  182|  1.81k|        uint8_t typeLength = valueLength | ipmi::fru::typeASCII;
  183|       |
  184|  1.81k|        data.emplace_back(typeLength);
  185|  1.81k|        std::copy(value.begin(), value.begin() + valueLength,
  186|  1.81k|                  std::back_inserter(data));
  187|  1.81k|    }
  188|  2.17k|    else
  189|  2.17k|    {
  190|       |        // set 0 size
  191|  2.17k|        data.emplace_back(typeLengthByteNull);
  192|  2.17k|    }
  193|  3.99k|}
_ZN4ipmi3fru15timeStringToRawERKNSt3__112basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE:
  196|    282|{
  197|       |    // TODO: For non-US region timestamps, pass in region information for the
  198|       |    // FRU to avoid the month/day swap.
  199|       |    // 2017-02-24 - 13:59:00, Tue Nov 20 23:08:00 2018, 20251224T064200Z
  200|    282|    static const std::vector<std::string> patterns = {
  201|    282|        "%Y-%m-%d - %H:%M:%S", "%a %b %d %H:%M:%S %Y", "%Y%m%dT%H%M%SZ"};
  202|       |
  203|    282|    std::tm time = {};
  204|       |
  205|    282|    for (const auto& pattern : patterns)
  ------------------
  |  Branch (205:30): [True: 715, False: 133]
  ------------------
  206|    715|    {
  207|    715|        std::istringstream timeStream(input);
  208|    715|        timeStream >> std::get_time(&time, pattern.c_str());
  209|    715|        if (!timeStream.fail())
  ------------------
  |  Branch (209:13): [True: 149, False: 566]
  ------------------
  210|    149|        {
  211|    149|            break;
  212|    149|        }
  213|    715|    }
  214|       |
  215|    282|    return timegm(&time);
  216|    282|}
_ZN4ipmi3fru13appendMfgDateERKNSt3__13mapINS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES8_NS1_4lessIS8_EENS6_INS1_4pairIKS8_S8_EEEEEERNS1_6vectorIhNS6_IhEEEE:
  225|    444|{
  226|       |    // MFG Date/Time
  227|    444|    auto iter = propMap.find(buildDate);
  228|    444|    if ((iter != propMap.end()) && (iter->second.size() > 0))
  ------------------
  |  Branch (228:9): [True: 282, False: 162]
  |  Branch (228:9): [True: 282, False: 162]
  |  Branch (228:36): [True: 282, False: 0]
  ------------------
  229|    282|    {
  230|    282|        std::time_t raw = timeStringToRaw(iter->second);
  231|       |
  232|       |        // From FRU Spec:
  233|       |        // "Mfg. Date / Time
  234|       |        // Number of minutes from 0:00 hrs 1/1/96.
  235|       |        // LSbyte first (little endian)
  236|       |        // 00_00_00h = unspecified."
  237|    282|        if ((raw >= secs_from_1970_1996) && (raw <= secsToMaxMfgdate))
  ------------------
  |  Branch (237:13): [True: 85, False: 197]
  |  Branch (237:45): [True: 46, False: 39]
  ------------------
  238|     46|        {
  239|     46|            raw -= secs_from_1970_1996;
  240|     46|            raw /= secs_per_min;
  241|     46|            uint8_t fru_raw[3];
  242|     46|            fru_raw[0] = raw & 0xFF;
  243|     46|            fru_raw[1] = (raw >> 8) & 0xFF;
  244|     46|            fru_raw[2] = (raw >> 16) & 0xFF;
  245|     46|            std::copy(fru_raw, fru_raw + 3, std::back_inserter(data));
  246|     46|            return;
  247|     46|        }
  248|    236|        std::fprintf(stderr, "MgfDate invalid date: %u secs since UNIX epoch\n",
  249|    236|                     static_cast<unsigned int>(raw));
  250|    236|    }
  251|       |    // Blank date
  252|    398|    data.emplace_back(0);
  253|    398|    data.emplace_back(0);
  254|    398|    data.emplace_back(0);
  255|    398|}
_ZN4ipmi3fru24buildCommonHeaderSectionERKjRtRNSt3__16vectorIhNS4_9allocatorIhEEEE:
  266|  2.23k|{
  267|       |    // Check if data for internal use section populated
  268|  2.23k|    if (infoAreaSize == 0)
  ------------------
  |  Branch (268:9): [True: 1.19k, False: 1.04k]
  ------------------
  269|  1.19k|    {
  270|       |        // Indicate record not present
  271|  1.19k|        data.emplace_back(recordNotPresent);
  272|  1.19k|    }
  273|  1.04k|    else
  274|  1.04k|    {
  275|       |        // offset should be multiple of 8.
  276|  1.04k|        auto remainder = offset % recordUnitOfMeasurement;
  277|       |        // add the padding bytes in the offset so that offset
  278|       |        // will be multiple of 8 byte.
  279|  1.04k|        offset += (remainder > 0) ? recordUnitOfMeasurement - remainder : 0;
  ------------------
  |  Branch (279:19): [True: 0, False: 1.04k]
  ------------------
  280|       |        // Place data to define offset to area data section
  281|  1.04k|        data.emplace_back(offset / recordUnitOfMeasurement);
  282|       |
  283|  1.04k|        offset += infoAreaSize;
  284|  1.04k|    }
  285|  2.23k|}
_ZN4ipmi3fru20buildChassisInfoAreaERKNSt3__13mapINS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES8_NS1_4lessIS8_EENS6_INS1_4pairIKS8_S8_EEEEEE:
  294|    257|{
  295|    257|    FruAreaData fruAreaData;
  296|    257|    if (!propMap.empty())
  ------------------
  |  Branch (296:9): [True: 257, False: 0]
  ------------------
  297|    257|    {
  298|       |        // Set formatting data that goes at the beginning of the record
  299|    257|        preFormatProcessing(false, fruAreaData);
  300|       |
  301|       |        // chassis type
  302|    257|        appendChassisType(propMap, fruAreaData);
  303|       |
  304|       |        // Chasiss part number, in config.yaml it is configured as model
  305|    257|        appendData(modelNumber, propMap, fruAreaData);
  306|       |
  307|       |        // Board serial number
  308|    257|        appendData(serialNumber, propMap, fruAreaData);
  309|       |
  310|       |        // Indicate End of Custom Fields
  311|    257|        fruAreaData.emplace_back(endOfCustomFields);
  312|       |
  313|       |        // Complete record data formatting
  314|    257|        postFormatProcessing(fruAreaData);
  315|    257|    }
  316|    257|    return fruAreaData;
  317|    257|}
_ZN4ipmi3fru18buildBoardInfoAreaERKNSt3__13mapINS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES8_NS1_4lessIS8_EENS6_INS1_4pairIKS8_S8_EEEEEE:
  326|    444|{
  327|    444|    FruAreaData fruAreaData;
  328|    444|    if (!propMap.empty())
  ------------------
  |  Branch (328:9): [True: 444, False: 0]
  ------------------
  329|    444|    {
  330|    444|        preFormatProcessing(true, fruAreaData);
  331|       |
  332|       |        // Manufacturing date
  333|    444|        appendMfgDate(propMap, fruAreaData);
  334|       |
  335|       |        // manufacturer
  336|    444|        appendData(manufacturer, propMap, fruAreaData);
  337|       |
  338|       |        // Product name/Pretty name
  339|    444|        appendData(prettyName, propMap, fruAreaData);
  340|       |
  341|       |        // Board serial number
  342|    444|        appendData(serialNumber, propMap, fruAreaData);
  343|       |
  344|       |        // Board part number
  345|    444|        appendData(partNumber, propMap, fruAreaData);
  346|       |
  347|       |        // FRU File ID - Empty
  348|    444|        fruAreaData.emplace_back(typeLengthByteNull);
  349|       |
  350|       |        // Empty FRU File ID bytes
  351|    444|        fruAreaData.emplace_back(recordNotPresent);
  352|       |
  353|       |        // End of custom fields
  354|    444|        fruAreaData.emplace_back(endOfCustomFields);
  355|       |
  356|    444|        postFormatProcessing(fruAreaData);
  357|    444|    }
  358|    444|    return fruAreaData;
  359|    444|}
_ZN4ipmi3fru20buildProductInfoAreaERKNSt3__13mapINS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEES8_NS1_4lessIS8_EENS6_INS1_4pairIKS8_S8_EEEEEE:
  368|    341|{
  369|    341|    FruAreaData fruAreaData;
  370|    341|    if (!propMap.empty())
  ------------------
  |  Branch (370:9): [True: 341, False: 0]
  ------------------
  371|    341|    {
  372|       |        // Set formatting data that goes at the beginning of the record
  373|    341|        preFormatProcessing(true, fruAreaData);
  374|       |
  375|       |        // manufacturer
  376|    341|        appendData(manufacturer, propMap, fruAreaData);
  377|       |
  378|       |        // Product name/Pretty name
  379|    341|        appendData(prettyName, propMap, fruAreaData);
  380|       |
  381|       |        // Product part/model number
  382|    341|        appendData(modelNumber, propMap, fruAreaData);
  383|       |
  384|       |        // Product version
  385|    341|        appendData(version, propMap, fruAreaData);
  386|       |
  387|       |        // Serial Number
  388|    341|        appendData(serialNumber, propMap, fruAreaData);
  389|       |
  390|       |        // Add Asset Tag
  391|    341|        fruAreaData.emplace_back(recordNotPresent);
  392|       |
  393|       |        // FRU File ID - Empty
  394|    341|        fruAreaData.emplace_back(typeLengthByteNull);
  395|       |
  396|       |        // Empty FRU File ID bytes
  397|    341|        fruAreaData.emplace_back(recordNotPresent);
  398|       |
  399|       |        // End of custom fields
  400|    341|        fruAreaData.emplace_back(endOfCustomFields);
  401|       |
  402|    341|        postFormatProcessing(fruAreaData);
  403|    341|    }
  404|    341|    return fruAreaData;
  405|    341|}
_ZN4ipmi3fru16buildFruAreaDataERKNSt3__13mapINS1_12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEENS2_IS8_S8_NS1_4lessIS8_EENS6_INS1_4pairIKS8_S8_EEEEEESA_NS6_INSB_ISC_SF_EEEEEE:
  408|    746|{
  409|    746|    FruAreaData combFruArea{};
  410|       |    // Now build common header with data for this FRU Inv Record
  411|       |    // Use this variable to increment size of header as we go along to determine
  412|       |    // offset for the subsequent area offsets
  413|    746|    uint16_t curDataOffset = commonHeaderFormatSize;
  414|       |    // First byte is id for version of FRU Info Storage Spec used
  415|    746|    combFruArea.emplace_back(specVersion);
  416|       |
  417|       |    // 2nd byte is offset to internal use data
  418|    746|    combFruArea.emplace_back(recordNotPresent);
  419|       |
  420|       |    // 3rd byte is offset to chassis data
  421|    746|    FruAreaData chassisArea;
  422|    746|    auto chassisIt = inventory.find(chassis);
  423|    746|    if (chassisIt != inventory.end())
  ------------------
  |  Branch (423:9): [True: 257, False: 489]
  ------------------
  424|    257|    {
  425|    257|        chassisArea = buildChassisInfoArea(chassisIt->second);
  426|    257|    }
  427|       |    // update the offset to chassis data.
  428|    746|    buildCommonHeaderSection(chassisArea.size(), curDataOffset, combFruArea);
  429|       |
  430|       |    // 4th byte is offset to board data
  431|    746|    FruAreaData boardArea;
  432|    746|    auto boardIt = inventory.find(board);
  433|    746|    if (boardIt != inventory.end())
  ------------------
  |  Branch (433:9): [True: 444, False: 302]
  ------------------
  434|    444|    {
  435|    444|        boardArea = buildBoardInfoArea(boardIt->second);
  436|    444|    }
  437|       |    // update the offset to the board data.
  438|    746|    buildCommonHeaderSection(boardArea.size(), curDataOffset, combFruArea);
  439|       |
  440|       |    // 5th byte is offset to product data
  441|    746|    FruAreaData prodArea;
  442|    746|    auto prodIt = inventory.find(product);
  443|    746|    if (prodIt != inventory.end())
  ------------------
  |  Branch (443:9): [True: 341, False: 405]
  ------------------
  444|    341|    {
  445|    341|        prodArea = buildProductInfoArea(prodIt->second);
  446|    341|    }
  447|       |    // update the offset to the product data.
  448|    746|    buildCommonHeaderSection(prodArea.size(), curDataOffset, combFruArea);
  449|       |
  450|       |    // 6th byte is offset to multirecord data
  451|    746|    combFruArea.emplace_back(recordNotPresent);
  452|       |
  453|       |    // 7th byte is PAD
  454|    746|    combFruArea.emplace_back(recordNotPresent);
  455|       |
  456|       |    // 8th (Final byte of Header Format) is the checksum
  457|    746|    appendDataChecksum(combFruArea);
  458|       |
  459|       |    // Combine everything into one full IPMI FRU specification Record
  460|       |    // add chassis use area data
  461|    746|    combFruArea.insert(combFruArea.end(), chassisArea.begin(),
  462|    746|                       chassisArea.end());
  463|       |
  464|       |    // add board area data
  465|    746|    combFruArea.insert(combFruArea.end(), boardArea.begin(), boardArea.end());
  466|       |
  467|       |    // add product use area data
  468|    746|    combFruArea.insert(combFruArea.end(), prodArea.begin(), prodArea.end());
  469|       |
  470|       |    // If area is smaller than the minimum size, pad it. This enables ipmitool
  471|       |    // to update the FRU blob with values longer than the original payload.
  472|    746|    if (combFruArea.size() < fruMinSize)
  ------------------
  |  Branch (472:9): [True: 746, False: 0]
  ------------------
  473|    746|    {
  474|    746|        combFruArea.resize(fruMinSize, fruPadValue);
  475|    746|    }
  476|       |
  477|    746|    return combFruArea;
  478|    746|}

_ZN3lg23logILNS_5levelE3EJRA6_KcRNSt3__112basic_stringIcNS5_11char_traitsIcEENS5_9allocatorIcEEEEEEC2EPS2_RKNS_7details10header_strESC_RKNS5_15source_locationE:
   44|    112|        log(s, msg, std::forward<details::header_str_conversion_t<Ts&&>>(ts)...)
   45|    112|    {}
_ZN3lg23logILNS_5levelE3EJRA6_KcRNSt3__112basic_stringIcNS5_11char_traitsIcEENS5_9allocatorIcEEEEEEC2ERKNS5_15source_locationEPS2_RKNS_7details10header_strESC_:
   29|    112|    {
   30|    112|        details::log_conversion::start(
   31|    112|            S, s, msg,
   32|    112|            std::forward<details::header_str_conversion_t<Ts&&>>(ts)...);
   33|    112|    }

_ZN3lg27details14log_conversion5startIJRKNS0_10header_strERNSt3__112basic_stringIcNS6_11char_traitsIcEENS6_9allocatorIcEEEEEEEvNS_5levelERKNS6_15source_locationEPKcDpOT_:
  453|    112|    {
  454|       |        // If there are no arguments (ie. just a message), then skip processing
  455|       |        // and call `done` directly.
  456|       |        if constexpr (sizeof...(Ts) == 0)
  457|       |        {
  458|       |            done(l, s, msg);
  459|       |        }
  460|       |        // Handle all the Ts by recursively calling 'step'.
  461|       |        else
  462|    112|        {
  463|    112|            step(std::forward_as_tuple(l, s, msg), ts...);
  464|    112|        }
  465|    112|    }
_ZN3lg27details14log_conversion4stepIJRNS_5levelERKNSt3__115source_locationERPKcERNS5_12basic_stringIcNS5_11char_traitsIcEENS5_9allocatorIcEEEEJEEEvONS5_5tupleIJDpT_EEERKNS0_10header_strEOT0_DpOT1_:
  404|    112|    {
  405|    112|        static_assert(!std::is_same_v<header_str, std::decay_t<V>>,
  406|    112|                      "Found header_str as value; suggest using std::string to "
  407|    112|                      "avoid unintended conversion.");
  408|       |        // These two if conditions are similar, except that one calls 'done'
  409|       |        // since Ss is empty and the other calls the next 'step'.
  410|       |
  411|       |        // 1. Call `log_convert` on {h, <empty flags>, v} for proper conversion.
  412|       |        // 2. Append the results of `log_convert` into the already handled
  413|       |        //    arguments (in ts).
  414|       |        // 3. Call the next step in the chain to handle the remainder Ss.
  415|       |
  416|       |        if constexpr (sizeof...(Ss) == 0)
  417|    112|        {
  418|    112|            apply_done(std::tuple_cat(std::move(ts),
  419|    112|                                      log_convert(h.data(), log_flag<>{}, v)));
  420|       |        }
  421|       |        else
  422|       |        {
  423|       |            step(std::tuple_cat(std::move(ts),
  424|       |                                log_convert(h.data(), log_flag<>{}, v)),
  425|       |                 ss...);
  426|       |        }
  427|    112|    }
_ZN3lg27details14log_conversion10apply_doneINSt3__15tupleIJRNS_5levelERKNS3_15source_locationERPKcSB_mSB_EEEEEvRKT_:
  354|    112|    {
  355|    112|        auto squash_string = [](auto& arg) -> decltype(auto) {
  356|    112|            if constexpr (std::is_same_v<const std::string&, decltype(arg)>)
  357|    112|            {
  358|    112|                return arg.data();
  359|    112|            }
  360|    112|            else
  361|    112|            {
  362|    112|                return arg;
  363|    112|            }
  364|    112|        };
  365|       |
  366|    112|        std::apply([squash_string](
  367|    112|                       const auto&... args) { done(squash_string(args)...); },
  368|    112|                   args_tuple);
  369|    112|    }
_ZZN3lg27details14log_conversion10apply_doneINSt3__15tupleIJRNS_5levelERKNS3_15source_locationERPKcSB_mSB_EEEEEvRKT_ENKUlDpRKT_E_clIJS5_S7_SB_SB_mSB_EEEDaSK_:
  367|    112|                       const auto&... args) { done(squash_string(args)...); },
_ZN3lg27details14log_conversion4doneIJRKPKcRKmS6_EEEvNS_5levelERKNSt3__115source_locationES4_DpOT_:
  341|    112|    {
  342|    112|        do_log(l, s, m, ts..., nullptr);
  343|    112|    }
_ZZN3lg27details14log_conversion10apply_doneINSt3__15tupleIJRNS_5levelERKNS3_15source_locationERPKcSB_mSB_EEEEEvRKT_ENKUlRSE_E_clIKS5_EEDcSH_:
  355|    112|        auto squash_string = [](auto& arg) -> decltype(auto) {
  356|       |            if constexpr (std::is_same_v<const std::string&, decltype(arg)>)
  357|       |            {
  358|       |                return arg.data();
  359|       |            }
  360|       |            else
  361|    112|            {
  362|    112|                return arg;
  363|    112|            }
  364|    112|        };
_ZZN3lg27details14log_conversion10apply_doneINSt3__15tupleIJRNS_5levelERKNS3_15source_locationERPKcSB_mSB_EEEEEvRKT_ENKUlRSE_E_clIS8_EEDcSH_:
  355|    112|        auto squash_string = [](auto& arg) -> decltype(auto) {
  356|       |            if constexpr (std::is_same_v<const std::string&, decltype(arg)>)
  357|       |            {
  358|       |                return arg.data();
  359|       |            }
  360|       |            else
  361|    112|            {
  362|    112|                return arg;
  363|    112|            }
  364|    112|        };
_ZZN3lg27details14log_conversion10apply_doneINSt3__15tupleIJRNS_5levelERKNS3_15source_locationERPKcSB_mSB_EEEEEvRKT_ENKUlRSE_E_clIKSB_EEDcSH_:
  355|    336|        auto squash_string = [](auto& arg) -> decltype(auto) {
  356|       |            if constexpr (std::is_same_v<const std::string&, decltype(arg)>)
  357|       |            {
  358|       |                return arg.data();
  359|       |            }
  360|       |            else
  361|    336|            {
  362|    336|                return arg;
  363|    336|            }
  364|    336|        };
_ZZN3lg27details14log_conversion10apply_doneINSt3__15tupleIJRNS_5levelERKNS3_15source_locationERPKcSB_mSB_EEEEEvRKT_ENKUlRSE_E_clIKmEEDcSH_:
  355|    112|        auto squash_string = [](auto& arg) -> decltype(auto) {
  356|       |            if constexpr (std::is_same_v<const std::string&, decltype(arg)>)
  357|       |            {
  358|       |                return arg.data();
  359|       |            }
  360|       |            else
  361|    112|            {
  362|    112|                return arg;
  363|    112|            }
  364|    112|        };
ipmi_fru_info_area.cpp:_ZN3lg27detailsL11log_convertITpTkNS0_9log_flagsEJETkNS0_16string_like_typeENSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEEEDaPKcNS0_8log_flagIJDpT_EEERKT0_:
  187|    112|{
  188|       |    // Compile-time checks for valid formatting flags.
  189|    112|    prohibit(f, bin);
  190|    112|    prohibit(f, dec);
  191|    112|    prohibit(f, field16);
  192|    112|    prohibit(f, field32);
  193|    112|    prohibit(f, field64);
  194|    112|    prohibit(f, field8);
  195|    112|    prohibit(f, floating);
  196|    112|    prohibit(f, hex);
  197|    112|    prohibit(f, signed_val);
  198|    112|    prohibit(f, unsigned_val);
  199|       |
  200|       |    // Utiilty to handle conversion to a 'const char*' depending on V:
  201|       |    //  - 'const char*' and similar use static cast.
  202|       |    //  - 'std::filesystem::path' use c_str() function.
  203|       |    //  - 'std::string' and 'std::string_view' use data() function.
  204|    112|    auto str_data = [](const V& v) {
  205|    112|        if constexpr (std::is_same_v<const char*, std::decay_t<V>> ||
  206|    112|                      std::is_same_v<char*, std::decay_t<V>>)
  207|    112|        {
  208|    112|            return static_cast<const char*>(v);
  209|    112|        }
  210|    112|        else if constexpr (std::is_same_v<std::filesystem::path,
  211|    112|                                          std::decay_t<V>>)
  212|    112|        {
  213|    112|            return v.c_str();
  214|    112|        }
  215|    112|        else
  216|    112|        {
  217|    112|            return v.data();
  218|    112|        }
  219|    112|    };
  220|       |
  221|       |    // Add 'str' flag, force to 'const char*' for variadic passing.
  222|    112|    return std::make_tuple(h, (f | str).value, str_data(v));
  223|    112|}
ipmi_fru_info_area.cpp:_ZZN3lg27detailsL11log_convertITpTkNS0_9log_flagsEJETkNS0_16string_like_typeENSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEEEDaPKcNS0_8log_flagIJDpT_EEERKT0_ENKUlRKS8_E_clESJ_:
  204|    112|    auto str_data = [](const V& v) {
  205|       |        if constexpr (std::is_same_v<const char*, std::decay_t<V>> ||
  206|       |                      std::is_same_v<char*, std::decay_t<V>>)
  207|       |        {
  208|       |            return static_cast<const char*>(v);
  209|       |        }
  210|       |        else if constexpr (std::is_same_v<std::filesystem::path,
  211|       |                                          std::decay_t<V>>)
  212|       |        {
  213|       |            return v.c_str();
  214|       |        }
  215|       |        else
  216|    112|        {
  217|    112|            return v.data();
  218|    112|        }
  219|    112|    };

_ZN3lg27details8prohibitITpTkNS0_9log_flagsEJETkNS0_9log_flagsENS0_8flag_binEEEvNS0_8log_flagIJDpT_EEENS3_IJT0_EEE:
   38|    112|{
   39|    112|    static_assert(!(... || std::is_same_v<Fs, F>),
   40|    112|                  "Prohibited flag found for value type.");
   41|    112|}
_ZN3lg27details8prohibitITpTkNS0_9log_flagsEJETkNS0_9log_flagsENS0_8flag_decEEEvNS0_8log_flagIJDpT_EEENS3_IJT0_EEE:
   38|    112|{
   39|    112|    static_assert(!(... || std::is_same_v<Fs, F>),
   40|    112|                  "Prohibited flag found for value type.");
   41|    112|}
_ZN3lg27details8prohibitITpTkNS0_9log_flagsEJETkNS0_9log_flagsENS0_12flag_field16EEEvNS0_8log_flagIJDpT_EEENS3_IJT0_EEE:
   38|    112|{
   39|    112|    static_assert(!(... || std::is_same_v<Fs, F>),
   40|    112|                  "Prohibited flag found for value type.");
   41|    112|}
_ZN3lg27details8prohibitITpTkNS0_9log_flagsEJETkNS0_9log_flagsENS0_12flag_field32EEEvNS0_8log_flagIJDpT_EEENS3_IJT0_EEE:
   38|    112|{
   39|    112|    static_assert(!(... || std::is_same_v<Fs, F>),
   40|    112|                  "Prohibited flag found for value type.");
   41|    112|}
_ZN3lg27details8prohibitITpTkNS0_9log_flagsEJETkNS0_9log_flagsENS0_12flag_field64EEEvNS0_8log_flagIJDpT_EEENS3_IJT0_EEE:
   38|    112|{
   39|    112|    static_assert(!(... || std::is_same_v<Fs, F>),
   40|    112|                  "Prohibited flag found for value type.");
   41|    112|}
_ZN3lg27details8prohibitITpTkNS0_9log_flagsEJETkNS0_9log_flagsENS0_11flag_field8EEEvNS0_8log_flagIJDpT_EEENS3_IJT0_EEE:
   38|    112|{
   39|    112|    static_assert(!(... || std::is_same_v<Fs, F>),
   40|    112|                  "Prohibited flag found for value type.");
   41|    112|}
_ZN3lg27details8prohibitITpTkNS0_9log_flagsEJETkNS0_9log_flagsENS0_13flag_floatingEEEvNS0_8log_flagIJDpT_EEENS3_IJT0_EEE:
   38|    112|{
   39|    112|    static_assert(!(... || std::is_same_v<Fs, F>),
   40|    112|                  "Prohibited flag found for value type.");
   41|    112|}
_ZN3lg27details8prohibitITpTkNS0_9log_flagsEJETkNS0_9log_flagsENS0_8flag_hexEEEvNS0_8log_flagIJDpT_EEENS3_IJT0_EEE:
   38|    112|{
   39|    112|    static_assert(!(... || std::is_same_v<Fs, F>),
   40|    112|                  "Prohibited flag found for value type.");
   41|    112|}
_ZN3lg27details8prohibitITpTkNS0_9log_flagsEJETkNS0_9log_flagsENS0_15flag_signed_valEEEvNS0_8log_flagIJDpT_EEENS3_IJT0_EEE:
   38|    112|{
   39|    112|    static_assert(!(... || std::is_same_v<Fs, F>),
   40|    112|                  "Prohibited flag found for value type.");
   41|    112|}
_ZN3lg27details8prohibitITpTkNS0_9log_flagsEJETkNS0_9log_flagsENS0_17flag_unsigned_valEEEvNS0_8log_flagIJDpT_EEENS3_IJT0_EEE:
   38|    112|{
   39|    112|    static_assert(!(... || std::is_same_v<Fs, F>),
   40|    112|                  "Prohibited flag found for value type.");
   41|    112|}
_ZN3lg27detailsorITpTkNS0_9log_flagsEJETpTkNS0_9log_flagsEJNS0_8flag_strEEEEDaNS0_8log_flagIJDpT_EEENS3_IJDpT0_EEE:
   31|    112|{
   32|    112|    return details::log_flag<As..., Bs...>{};
   33|    112|}

_ZNK3lg27details10header_str4dataEv:
   58|    112|    {
   59|    112|        return value.data();
   60|    112|    }

LLVMFuzzerTestOneInput:
   45|    775|{
   46|    775|    if (size < 3)
  ------------------
  |  Branch (46:9): [True: 2, False: 773]
  ------------------
   47|      2|    {
   48|      2|        return 0;
   49|      2|    }
   50|       |
   51|       |    // Use first byte to decide which sections to populate
   52|    773|    uint8_t sectionMask = data[0];
   53|    773|    data++;
   54|    773|    size--;
   55|       |
   56|    773|    ipmi::fru::FruInventoryData inventory;
   57|       |
   58|       |    // Property keys used by the FRU builder
   59|    773|    const char* keys[] = {"Type",        "Model Number", "Serial Number",
   60|    773|                          "Manufacturer", "Mfg Date",    "Name",
   61|    773|                          "Part Number",  "Version"};
   62|       |
   63|       |    // Chassis section
   64|    773|    if (sectionMask & 0x01)
  ------------------
  |  Branch (64:9): [True: 265, False: 508]
  ------------------
   65|    265|    {
   66|    265|        ipmi::fru::PropertyMap props;
   67|    265|        for (const auto& key : keys)
  ------------------
  |  Branch (67:30): [True: 1.61k, False: 151]
  ------------------
   68|  1.61k|        {
   69|  1.61k|            std::string val;
   70|  1.61k|            if (!extractString(data, size, val))
  ------------------
  |  Branch (70:17): [True: 114, False: 1.49k]
  ------------------
   71|    114|            {
   72|    114|                break;
   73|    114|            }
   74|  1.49k|            if (!val.empty())
  ------------------
  |  Branch (74:17): [True: 834, False: 664]
  ------------------
   75|    834|            {
   76|    834|                props[key] = val;
   77|    834|            }
   78|  1.49k|        }
   79|    265|        if (!props.empty())
  ------------------
  |  Branch (79:13): [True: 257, False: 8]
  ------------------
   80|    257|        {
   81|    257|            inventory["Chassis"] = props;
   82|    257|        }
   83|    265|    }
   84|       |
   85|       |    // Board section
   86|    773|    if (sectionMask & 0x02)
  ------------------
  |  Branch (86:9): [True: 527, False: 246]
  ------------------
   87|    527|    {
   88|    527|        ipmi::fru::PropertyMap props;
   89|    527|        for (const auto& key : keys)
  ------------------
  |  Branch (89:30): [True: 3.05k, False: 195]
  ------------------
   90|  3.05k|        {
   91|  3.05k|            std::string val;
   92|  3.05k|            if (!extractString(data, size, val))
  ------------------
  |  Branch (92:17): [True: 332, False: 2.71k]
  ------------------
   93|    332|            {
   94|    332|                break;
   95|    332|            }
   96|  2.71k|            if (!val.empty())
  ------------------
  |  Branch (96:17): [True: 1.26k, False: 1.45k]
  ------------------
   97|  1.26k|            {
   98|  1.26k|                props[key] = val;
   99|  1.26k|            }
  100|  2.71k|        }
  101|    527|        if (!props.empty())
  ------------------
  |  Branch (101:13): [True: 444, False: 83]
  ------------------
  102|    444|        {
  103|    444|            inventory["Board"] = props;
  104|    444|        }
  105|    527|    }
  106|       |
  107|       |    // Product section
  108|    773|    if (sectionMask & 0x04)
  ------------------
  |  Branch (108:9): [True: 599, False: 174]
  ------------------
  109|    599|    {
  110|    599|        ipmi::fru::PropertyMap props;
  111|    599|        for (const auto& key : keys)
  ------------------
  |  Branch (111:30): [True: 2.21k, False: 118]
  ------------------
  112|  2.21k|        {
  113|  2.21k|            std::string val;
  114|  2.21k|            if (!extractString(data, size, val))
  ------------------
  |  Branch (114:17): [True: 481, False: 1.73k]
  ------------------
  115|    481|            {
  116|    481|                break;
  117|    481|            }
  118|  1.73k|            if (!val.empty())
  ------------------
  |  Branch (118:17): [True: 1.25k, False: 482]
  ------------------
  119|  1.25k|            {
  120|  1.25k|                props[key] = val;
  121|  1.25k|            }
  122|  1.73k|        }
  123|    599|        if (!props.empty())
  ------------------
  |  Branch (123:13): [True: 341, False: 258]
  ------------------
  124|    341|        {
  125|    341|            inventory["Product"] = props;
  126|    341|        }
  127|    599|    }
  128|       |
  129|    773|    if (!inventory.empty())
  ------------------
  |  Branch (129:9): [True: 746, False: 27]
  ------------------
  130|    746|    {
  131|    746|        ipmi::fru::buildFruAreaData(inventory);
  132|    746|    }
  133|       |
  134|    773|    return 0;
  135|    775|}
fuzz_fru_area.cpp:_ZL13extractStringRPKhRmRNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEE:
   24|  6.87k|{
   25|  6.87k|    if (remaining < 1)
  ------------------
  |  Branch (25:9): [True: 927, False: 5.95k]
  ------------------
   26|    927|    {
   27|    927|        return false;
   28|    927|    }
   29|  5.95k|    uint8_t len = data[0] % 64; // Cap at 63 to keep things reasonable
   30|  5.95k|    data++;
   31|  5.95k|    remaining--;
   32|       |
   33|  5.95k|    if (remaining < len)
  ------------------
  |  Branch (33:9): [True: 551, False: 5.39k]
  ------------------
   34|    551|    {
   35|    551|        len = static_cast<uint8_t>(remaining);
   36|    551|    }
   37|       |
   38|  5.95k|    out.assign(reinterpret_cast<const char*>(data), len);
   39|  5.95k|    data += len;
   40|  5.95k|    remaining -= len;
   41|  5.95k|    return true;
   42|  6.87k|}

_ZN3lg27details6do_logENS_5levelERKNSt3__115source_locationEPKcz:
   26|    112|{
   27|       |    // No-op: suppress all logging during fuzzing.
   28|    112|}

