_ZN4ipmi19getSensorAttributesEddRsRaS0_S1_Rb:
  154|    216|{
  155|    216|    if (!(std::isfinite(min)))
  ------------------
  |  Branch (155:9): [True: 4, False: 212]
  ------------------
  156|      4|    {
  157|      4|        lg2::error("getSensorAttributes: Min value is unusable");
  158|      4|        return false;
  159|      4|    }
  160|    212|    if (!(std::isfinite(max)))
  ------------------
  |  Branch (160:9): [True: 2, False: 210]
  ------------------
  161|      2|    {
  162|      2|        lg2::error("getSensorAttributes: Max value is unusable");
  163|      2|        return false;
  164|      2|    }
  165|       |
  166|       |    // Because NAN has already been tested for, this comparison works
  167|    210|    if (max <= min)
  ------------------
  |  Branch (167:9): [True: 6, False: 204]
  ------------------
  168|      6|    {
  169|      6|        lg2::error("getSensorAttributes: Max must be greater than min");
  170|      6|        return false;
  171|      6|    }
  172|       |
  173|       |    // Given min and max, we must solve for M, B, bExp, rExp
  174|       |    // y comes in from D-Bus (the actual sensor reading)
  175|       |    // x is calculated from y by scaleIPMIValueFromDouble() below
  176|       |    // If y is min, x should equal = 0 (or -128 if signed)
  177|       |    // If y is max, x should equal 255 (or 127 if signed)
  178|    204|    double fullRange = max - min;
  179|    204|    double lowestX;
  180|       |
  181|    204|    rExp = 0;
  182|    204|    bExp = 0;
  183|       |
  184|       |    // TODO(): The IPMI document is ambiguous, as to whether
  185|       |    // the resulting byte should be signed or unsigned,
  186|       |    // essentially leaving it up to the caller.
  187|       |    // The document just refers to it as "raw reading",
  188|       |    // or "byte of reading", without giving further details.
  189|       |    // Previous code set it signed if min was less than zero,
  190|       |    // so I'm sticking with that, until I learn otherwise.
  191|    204|    if (min < 0.0)
  ------------------
  |  Branch (191:9): [True: 108, False: 96]
  ------------------
  192|    108|    {
  193|       |        // TODO(): It would be worth experimenting with the range (-127,127),
  194|       |        // instead of the range (-128,127), because this
  195|       |        // would give good symmetry around zero, and make results look better.
  196|       |        // Divide by 254 instead of 255, and change -128 to -127 elsewhere.
  197|    108|        bSigned = true;
  198|    108|        lowestX = -128.0;
  199|    108|    }
  200|     96|    else
  201|     96|    {
  202|     96|        bSigned = false;
  203|     96|        lowestX = 0.0;
  204|     96|    }
  205|       |
  206|       |    // Step 1: Set y to (max - min), set x to 255, set B to 0, solve for M
  207|       |    // This works, regardless of signed or unsigned,
  208|       |    // because total range is the same.
  209|    204|    double dM = fullRange / 255.0;
  210|       |
  211|       |    // Step 2: Constrain M, and set rExp accordingly
  212|    204|    if (!(scaleFloatExp(dM, rExp)))
  ------------------
  |  Branch (212:9): [True: 8, False: 196]
  ------------------
  213|      8|    {
  214|      8|        lg2::error(
  215|      8|            "getSensorAttributes: Multiplier range exceeds scale (M={DM}, "
  216|      8|            "rExp={REXP})",
  217|      8|            "DM", dM, "REXP", rExp);
  218|      8|        return false;
  219|      8|    }
  220|       |
  221|    196|    mValue = static_cast<int16_t>(std::round(dM));
  222|       |
  223|    196|    normalizeIntExp(mValue, rExp, dM);
  224|       |
  225|       |    // The multiplier can not be zero, for obvious reasons
  226|    196|    if (mValue == 0)
  ------------------
  |  Branch (226:9): [True: 12, False: 184]
  ------------------
  227|     12|    {
  228|     12|        lg2::error("getSensorAttributes: Multiplier range below scale");
  229|     12|        return false;
  230|     12|    }
  231|       |
  232|       |    // Step 3: set y to min, set x to min, keep M and rExp, solve for B
  233|       |    // If negative, x will be -128 (the most negative possible byte), not 0
  234|       |
  235|       |    // Solve the IPMI equation for B, instead of y
  236|       |    // https://www.wolframalpha.com/input/?i=solve+y%3D%28%28M*x%29%2B%28B*%2810%5EE%29%29%29*%2810%5ER%29+for+B
  237|       |    // B = 10^(-rExp - bExp) (y - M 10^rExp x)
  238|       |    // TODO(): Compare with this alternative solution from SageMathCell
  239|       |    // https://sagecell.sagemath.org/?z=eJyrtC1LLNJQr1TX5KqAMCuATF8I0xfIdIIwnYDMIteKAggPxAIKJMEFkiACxfk5Zaka0ZUKtrYKGhq-CloKFZoK2goaTkCWhqGBgpaWAkilpqYmQgBklmasjoKTJgDAECTH&lang=sage&interacts=eJyLjgUAARUAuQ==
  240|    184|    double dB = std::pow(10.0, ((-rExp) - bExp)) *
  241|    184|                (min - ((dM * std::pow(10.0, rExp) * lowestX)));
  242|       |
  243|       |    // Step 4: Constrain B, and set bExp accordingly
  244|    184|    if (!(scaleFloatExp(dB, bExp)))
  ------------------
  |  Branch (244:9): [True: 2, False: 182]
  ------------------
  245|      2|    {
  246|      2|        lg2::error(
  247|      2|            "getSensorAttributes: Offset range exceeds scale (B={DB}, "
  248|      2|            "bExp={BEXP}) exceeds multiplier scale (M={DM}, rExp={REXP})",
  249|      2|            "DB", dB, "BEXP", bExp, "DM", dM, "REXP", rExp);
  250|      2|        return false;
  251|      2|    }
  252|       |
  253|    182|    bValue = static_cast<int16_t>(std::round(dB));
  254|       |
  255|    182|    normalizeIntExp(bValue, bExp, dB);
  256|       |
  257|       |    // Unlike the multiplier, it is perfectly OK for bValue to be zero
  258|    182|    return true;
  259|    184|}
_ZN4ipmi24scaleIPMIValueFromDoubleEdsasab:
  264|    182|{
  265|       |    // Avoid division by zero below
  266|    182|    if (mValue == 0)
  ------------------
  |  Branch (266:9): [True: 0, False: 182]
  ------------------
  267|      0|    {
  268|      0|        throw std::out_of_range("Scaling multiplier is uninitialized");
  269|      0|    }
  270|       |
  271|    182|    auto dM = static_cast<double>(mValue);
  272|    182|    auto dB = static_cast<double>(bValue);
  273|       |
  274|       |    // Solve the IPMI equation for x, instead of y
  275|       |    // https://www.wolframalpha.com/input/?i=solve+y%3D%28%28M*x%29%2B%28B*%2810%5EE%29%29%29*%2810%5ER%29+for+x
  276|       |    // x = (10^(-rExp) (y - B 10^(rExp + bExp)))/M and M 10^rExp!=0
  277|       |    // TODO(): Compare with this alternative solution from SageMathCell
  278|       |    // https://sagecell.sagemath.org/?z=eJyrtC1LLNJQr1TX5KqAMCuATF8I0xfIdIIwnYDMIteKAggPxAIKJMEFkiACxfk5Zaka0ZUKtrYKGhq-CloKFZoK2goaTkCWhqGBgpaWAkilpqYmQgBklmasDlAlAMB8JP0=&lang=sage&interacts=eJyLjgUAARUAuQ==
  279|    182|    double dX =
  280|    182|        (std::pow(10.0, -rExp) * (value - (dB * std::pow(10.0, rExp + bExp)))) /
  281|    182|        dM;
  282|       |
  283|    182|    auto scaledValue = static_cast<int32_t>(std::round(dX));
  284|       |
  285|    182|    int32_t minClamp;
  286|    182|    int32_t maxClamp;
  287|       |
  288|       |    // Because of rounding and integer truncation of scaling factors,
  289|       |    // sometimes the resulting byte is slightly out of range.
  290|       |    // Still allow this, but clamp the values to range.
  291|    182|    if (bSigned)
  ------------------
  |  Branch (291:9): [True: 92, False: 90]
  ------------------
  292|     92|    {
  293|     92|        minClamp = std::numeric_limits<int8_t>::lowest();
  294|     92|        maxClamp = std::numeric_limits<int8_t>::max();
  295|     92|    }
  296|     90|    else
  297|     90|    {
  298|     90|        minClamp = std::numeric_limits<uint8_t>::lowest();
  299|     90|        maxClamp = std::numeric_limits<uint8_t>::max();
  300|     90|    }
  301|       |
  302|    182|    auto clampedValue = std::clamp(scaledValue, minClamp, maxClamp);
  303|       |
  304|       |    // This works for both signed and unsigned,
  305|       |    // because it is the same underlying byte storage.
  306|    182|    return static_cast<uint8_t>(clampedValue);
  307|    182|}
_ZN4ipmi18getScaledIPMIValueEddd:
  311|    108|{
  312|    108|    int16_t mValue = 0;
  313|    108|    int8_t rExp = 0;
  314|    108|    int16_t bValue = 0;
  315|    108|    int8_t bExp = 0;
  316|    108|    bool bSigned = false;
  317|       |
  318|    108|    bool result =
  319|    108|        getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned);
  320|    108|    if (!result)
  ------------------
  |  Branch (320:9): [True: 17, False: 91]
  ------------------
  321|     17|    {
  322|     17|        throw std::runtime_error("Illegal sensor attributes");
  323|     17|    }
  324|       |
  325|     91|    return scaleIPMIValueFromDouble(value, mValue, rExp, bValue, bExp, bSigned);
  326|    108|}
sensorutils.cpp:_ZN4ipmiL13scaleFloatExpERdRa:
   49|    388|{
   50|       |    // Comparing with zero should be OK, zero is special in floating-point
   51|       |    // If base is exactly zero, no adjustment of the exponent is necessary
   52|    388|    if (base == 0.0)
  ------------------
  |  Branch (52:9): [True: 6, False: 382]
  ------------------
   53|      6|    {
   54|      6|        return true;
   55|      6|    }
   56|       |
   57|       |    // As long as base value is within allowed range, expand precision
   58|       |    // This will help to avoid loss when later rounding to integer
   59|  1.48k|    while (baseInRange(base))
  ------------------
  |  Branch (59:12): [True: 1.17k, False: 310]
  ------------------
   60|  1.17k|    {
   61|  1.17k|        if (expShift <= minInt4)
  ------------------
  |  Branch (61:13): [True: 72, False: 1.10k]
  ------------------
   62|     72|        {
   63|       |            // Already at the minimum expShift, can not decrement it more
   64|     72|            break;
   65|     72|        }
   66|       |
   67|       |        // Multiply by 10, but shift decimal point to the left, no net change
   68|  1.10k|        base *= 10.0;
   69|  1.10k|        --expShift;
   70|  1.10k|    }
   71|       |
   72|       |    // As long as base value is *not* within range, shrink precision
   73|       |    // This will pull base value closer to zero, thus within range
   74|  1.07k|    while (!(baseInRange(base)))
  ------------------
  |  Branch (74:12): [True: 702, False: 372]
  ------------------
   75|    702|    {
   76|    702|        if (expShift >= maxInt4)
  ------------------
  |  Branch (76:13): [True: 10, False: 692]
  ------------------
   77|     10|        {
   78|       |            // Already at the maximum expShift, can not increment it more
   79|     10|            break;
   80|     10|        }
   81|       |
   82|       |        // Divide by 10, but shift decimal point to the right, no net change
   83|    692|        base /= 10.0;
   84|    692|        ++expShift;
   85|    692|    }
   86|       |
   87|       |    // If the above loop was not able to pull it back within range,
   88|       |    // the base value is beyond what expShift can represent, return false.
   89|    382|    return baseInRange(base);
   90|    388|}
sensorutils.cpp:_ZN4ipmiL11baseInRangeEd:
   29|  2.93k|{
   30|  2.93k|    auto min10 = static_cast<double>(minInt10);
   31|  2.93k|    auto max10 = static_cast<double>(maxInt10);
   32|       |
   33|  2.93k|    return ((base >= min10) && (base <= max10));
  ------------------
  |  Branch (33:13): [True: 2.70k, False: 232]
  |  Branch (33:32): [True: 1.91k, False: 790]
  ------------------
   34|  2.93k|}
sensorutils.cpp:_ZN4ipmiL15normalizeIntExpERsRaRd:
  106|    378|{
  107|    378|    for (;;)
  108|    468|    {
  109|       |        // If zero, already normalized, ensure exponent also zero
  110|    468|        if (ibase == 0)
  ------------------
  |  Branch (110:13): [True: 52, False: 416]
  ------------------
  111|     52|        {
  112|     52|            expShift = 0;
  113|     52|            break;
  114|     52|        }
  115|       |
  116|       |        // If not cleanly divisible by 10, already normalized
  117|    416|        if ((ibase % 10) != 0)
  ------------------
  |  Branch (117:13): [True: 314, False: 102]
  ------------------
  118|    314|        {
  119|    314|            break;
  120|    314|        }
  121|       |
  122|       |        // If exponent already at max, already normalized
  123|    102|        if (expShift >= maxInt4)
  ------------------
  |  Branch (123:13): [True: 12, False: 90]
  ------------------
  124|     12|        {
  125|     12|            break;
  126|     12|        }
  127|       |
  128|       |        // Bring values closer to zero, correspondingly shift exponent,
  129|       |        // without changing the underlying number that this all represents,
  130|       |        // similar to what is done by scaleFloatExp().
  131|       |        // The floating-point base must be kept in sync with the integer base,
  132|       |        // as both floating-point and integer share the same exponent.
  133|     90|        ibase /= 10;
  134|     90|        dbase /= 10.0;
  135|     90|        ++expShift;
  136|     90|    }
  137|    378|}

_ZN3lg23logILNS_5levelE3EJEEC2EPKcRKNSt3__115source_locationE:
   44|     24|        log(s, msg, std::forward<details::header_str_conversion_t<Ts&&>>(ts)...)
   45|     24|    {}
_ZN3lg23logILNS_5levelE3EJEEC2ERKNSt3__115source_locationEPKc:
   29|     24|    {
   30|     24|        details::log_conversion::start(
   31|     24|            S, s, msg,
   32|     24|            std::forward<details::header_str_conversion_t<Ts&&>>(ts)...);
   33|     24|    }
_ZN3lg23logILNS_5levelE3EJRA3_KcRdRA5_S2_RaEEC2EPS2_RKNS_7details10header_strES5_SE_S8_RKNSt3__115source_locationE:
   44|      8|        log(s, msg, std::forward<details::header_str_conversion_t<Ts&&>>(ts)...)
   45|      8|    {}
_ZN3lg23logILNS_5levelE3EJRA3_KcRdRA5_S2_RaEEC2ERKNSt3__115source_locationEPS2_RKNS_7details10header_strES5_SI_S8_:
   29|      8|    {
   30|      8|        details::log_conversion::start(
   31|      8|            S, s, msg,
   32|      8|            std::forward<details::header_str_conversion_t<Ts&&>>(ts)...);
   33|      8|    }
_ZN3lg23logILNS_5levelE3EJRA3_KcRdRA5_S2_RaS4_S5_S7_S8_EEC2EPS2_RKNS_7details10header_strES5_SE_S8_SE_S5_SE_S8_RKNSt3__115source_locationE:
   44|      2|        log(s, msg, std::forward<details::header_str_conversion_t<Ts&&>>(ts)...)
   45|      2|    {}
_ZN3lg23logILNS_5levelE3EJRA3_KcRdRA5_S2_RaS4_S5_S7_S8_EEC2ERKNSt3__115source_locationEPS2_RKNS_7details10header_strES5_SI_S8_SI_S5_SI_S8_:
   29|      2|    {
   30|      2|        details::log_conversion::start(
   31|      2|            S, s, msg,
   32|      2|            std::forward<details::header_str_conversion_t<Ts&&>>(ts)...);
   33|      2|    }

_ZN3lg27details14log_conversion5startIJEEEvNS_5levelERKNSt3__115source_locationEPKcDpOT_:
  453|     24|    {
  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|     24|        {
  458|     24|            done(l, s, msg);
  459|       |        }
  460|       |        // Handle all the Ts by recursively calling 'step'.
  461|       |        else
  462|       |        {
  463|       |            step(std::forward_as_tuple(l, s, msg), ts...);
  464|       |        }
  465|     24|    }
_ZN3lg27details14log_conversion4doneIJEEEvNS_5levelERKNSt3__115source_locationEPKcDpOT_:
  341|     24|    {
  342|     24|        do_log(l, s, m, ts..., nullptr);
  343|     24|    }
_ZN3lg27details14log_conversion5startIJRKNS0_10header_strERdS5_RaEEEvNS_5levelERKNSt3__115source_locationEPKcDpOT_:
  453|      8|    {
  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|      8|        {
  463|      8|            step(std::forward_as_tuple(l, s, msg), ts...);
  464|      8|        }
  465|      8|    }
_ZN3lg27details14log_conversion4stepIJRNS_5levelERKNSt3__115source_locationERPKcERdJRKNS0_10header_strERaEEEvONS5_5tupleIJDpT_EEESF_OT0_DpOT1_:
  404|      8|    {
  405|      8|        static_assert(!std::is_same_v<header_str, std::decay_t<V>>,
  406|      8|                      "Found header_str as value; suggest using std::string to "
  407|      8|                      "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|       |        {
  418|       |            apply_done(std::tuple_cat(std::move(ts),
  419|       |                                      log_convert(h.data(), log_flag<>{}, v)));
  420|       |        }
  421|       |        else
  422|      8|        {
  423|      8|            step(std::tuple_cat(std::move(ts),
  424|      8|                                log_convert(h.data(), log_flag<>{}, v)),
  425|      8|                 ss...);
  426|      8|        }
  427|      8|    }
_ZN3lg27details14log_conversion4stepIJRNS_5levelERKNSt3__115source_locationERPKcSA_mdERaJEEEvONS5_5tupleIJDpT_EEERKNS0_10header_strEOT0_DpOT1_:
  404|      8|    {
  405|      8|        static_assert(!std::is_same_v<header_str, std::decay_t<V>>,
  406|      8|                      "Found header_str as value; suggest using std::string to "
  407|      8|                      "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|      8|        {
  418|      8|            apply_done(std::tuple_cat(std::move(ts),
  419|      8|                                      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|      8|    }
_ZN3lg27details14log_conversion10apply_doneINSt3__15tupleIJRNS_5levelERKNS3_15source_locationERPKcSB_mdSB_mlEEEEEvRKT_:
  354|      8|    {
  355|      8|        auto squash_string = [](auto& arg) -> decltype(auto) {
  356|      8|            if constexpr (std::is_same_v<const std::string&, decltype(arg)>)
  357|      8|            {
  358|      8|                return arg.data();
  359|      8|            }
  360|      8|            else
  361|      8|            {
  362|      8|                return arg;
  363|      8|            }
  364|      8|        };
  365|       |
  366|      8|        std::apply([squash_string](
  367|      8|                       const auto&... args) { done(squash_string(args)...); },
  368|      8|                   args_tuple);
  369|      8|    }
_ZZN3lg27details14log_conversion10apply_doneINSt3__15tupleIJRNS_5levelERKNS3_15source_locationERPKcSB_mdSB_mlEEEEEvRKT_ENKUlDpRKT_E_clIJS5_S7_SB_SB_mdSB_mlEEEDaSK_:
  367|      8|                       const auto&... args) { done(squash_string(args)...); },
_ZN3lg27details14log_conversion4doneIJRKPKcRKmRKdS6_S8_RKlEEEvNS_5levelERKNSt3__115source_locationES4_DpOT_:
  341|      8|    {
  342|      8|        do_log(l, s, m, ts..., nullptr);
  343|      8|    }
_ZZN3lg27details14log_conversion10apply_doneINSt3__15tupleIJRNS_5levelERKNS3_15source_locationERPKcSB_mdSB_mlEEEEEvRKT_ENKUlRSE_E_clIKS5_EEDcSH_:
  355|      8|        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|      8|            {
  362|      8|                return arg;
  363|      8|            }
  364|      8|        };
_ZZN3lg27details14log_conversion10apply_doneINSt3__15tupleIJRNS_5levelERKNS3_15source_locationERPKcSB_mdSB_mlEEEEEvRKT_ENKUlRSE_E_clIS8_EEDcSH_:
  355|      8|        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|      8|            {
  362|      8|                return arg;
  363|      8|            }
  364|      8|        };
_ZZN3lg27details14log_conversion10apply_doneINSt3__15tupleIJRNS_5levelERKNS3_15source_locationERPKcSB_mdSB_mlEEEEEvRKT_ENKUlRSE_E_clIKSB_EEDcSH_:
  355|     24|        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|     24|            {
  362|     24|                return arg;
  363|     24|            }
  364|     24|        };
_ZZN3lg27details14log_conversion10apply_doneINSt3__15tupleIJRNS_5levelERKNS3_15source_locationERPKcSB_mdSB_mlEEEEEvRKT_ENKUlRSE_E_clIKmEEDcSH_:
  355|     16|        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|     16|            {
  362|     16|                return arg;
  363|     16|            }
  364|     16|        };
_ZZN3lg27details14log_conversion10apply_doneINSt3__15tupleIJRNS_5levelERKNS3_15source_locationERPKcSB_mdSB_mlEEEEEvRKT_ENKUlRSE_E_clIKdEEDcSH_:
  355|      8|        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|      8|            {
  362|      8|                return arg;
  363|      8|            }
  364|      8|        };
_ZZN3lg27details14log_conversion10apply_doneINSt3__15tupleIJRNS_5levelERKNS3_15source_locationERPKcSB_mdSB_mlEEEEEvRKT_ENKUlRSE_E_clIKlEEDcSH_:
  355|      8|        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|      8|            {
  362|      8|                return arg;
  363|      8|            }
  364|      8|        };
sensorutils.cpp:_ZN3lg27detailsL11log_convertITpTkNS0_9log_flagsEJETkNSt3__115signed_integralEaEEDaPKcNS0_8log_flagIJDpT_EEET0_:
  111|     12|{
  112|       |    // Compile-time checks for valid formatting flags.
  113|     12|    prohibit(f, floating);
  114|     12|    prohibit(f, str);
  115|     12|    prohibit(f, unsigned_val);
  116|       |
  117|     12|    one_from_set(f, dec | hex | bin);
  118|     12|    one_from_set(f, field8 | field16 | field32 | field64);
  119|       |
  120|       |    // Add 'signed' flag, force to int64_t for variadic passing.
  121|     12|    return std::make_tuple(h, (f | signed_val).value, static_cast<int64_t>(v));
  122|     12|}
sensorutils.cpp:_ZN3lg27detailsL11log_convertITpTkNS0_9log_flagsEJETkNSt3__114floating_pointEdEEDaPKcNS0_8log_flagIJDpT_EEET0_:
  147|     12|{
  148|       |    // Compile-time checks for valid formatting flags.
  149|     12|    prohibit(f, bin);
  150|     12|    prohibit(f, dec);
  151|     12|    prohibit(f, field16);
  152|     12|    prohibit(f, field32);
  153|     12|    prohibit(f, field64);
  154|     12|    prohibit(f, field8);
  155|     12|    prohibit(f, hex);
  156|     12|    prohibit(f, signed_val);
  157|     12|    prohibit(f, str);
  158|     12|    prohibit(f, unsigned_val);
  159|       |
  160|       |    // Add 'floating' flag, force to double for variadic passing.
  161|     12|    return std::make_tuple(h, (f | floating).value, static_cast<double>(v));
  162|     12|}
_ZN3lg27details14log_conversion5startIJRKNS0_10header_strERdS5_RaS5_S6_S5_S7_EEEvNS_5levelERKNSt3__115source_locationEPKcDpOT_:
  453|      2|    {
  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|      2|        {
  463|      2|            step(std::forward_as_tuple(l, s, msg), ts...);
  464|      2|        }
  465|      2|    }
_ZN3lg27details14log_conversion4stepIJRNS_5levelERKNSt3__115source_locationERPKcERdJRKNS0_10header_strERaSF_SC_SF_SG_EEEvONS5_5tupleIJDpT_EEESF_OT0_DpOT1_:
  404|      2|    {
  405|      2|        static_assert(!std::is_same_v<header_str, std::decay_t<V>>,
  406|      2|                      "Found header_str as value; suggest using std::string to "
  407|      2|                      "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|       |        {
  418|       |            apply_done(std::tuple_cat(std::move(ts),
  419|       |                                      log_convert(h.data(), log_flag<>{}, v)));
  420|       |        }
  421|       |        else
  422|      2|        {
  423|      2|            step(std::tuple_cat(std::move(ts),
  424|      2|                                log_convert(h.data(), log_flag<>{}, v)),
  425|      2|                 ss...);
  426|      2|        }
  427|      2|    }
_ZN3lg27details14log_conversion4stepIJRNS_5levelERKNSt3__115source_locationERPKcSA_mdERaJRKNS0_10header_strERdSF_SC_EEEvONS5_5tupleIJDpT_EEESF_OT0_DpOT1_:
  404|      2|    {
  405|      2|        static_assert(!std::is_same_v<header_str, std::decay_t<V>>,
  406|      2|                      "Found header_str as value; suggest using std::string to "
  407|      2|                      "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|       |        {
  418|       |            apply_done(std::tuple_cat(std::move(ts),
  419|       |                                      log_convert(h.data(), log_flag<>{}, v)));
  420|       |        }
  421|       |        else
  422|      2|        {
  423|      2|            step(std::tuple_cat(std::move(ts),
  424|      2|                                log_convert(h.data(), log_flag<>{}, v)),
  425|      2|                 ss...);
  426|      2|        }
  427|      2|    }
_ZN3lg27details14log_conversion4stepIJRNS_5levelERKNSt3__115source_locationERPKcSA_mdSA_mlERdJRKNS0_10header_strERaEEEvONS5_5tupleIJDpT_EEESF_OT0_DpOT1_:
  404|      2|    {
  405|      2|        static_assert(!std::is_same_v<header_str, std::decay_t<V>>,
  406|      2|                      "Found header_str as value; suggest using std::string to "
  407|      2|                      "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|       |        {
  418|       |            apply_done(std::tuple_cat(std::move(ts),
  419|       |                                      log_convert(h.data(), log_flag<>{}, v)));
  420|       |        }
  421|       |        else
  422|      2|        {
  423|      2|            step(std::tuple_cat(std::move(ts),
  424|      2|                                log_convert(h.data(), log_flag<>{}, v)),
  425|      2|                 ss...);
  426|      2|        }
  427|      2|    }
_ZN3lg27details14log_conversion4stepIJRNS_5levelERKNSt3__115source_locationERPKcSA_mdSA_mlSA_mdERaJEEEvONS5_5tupleIJDpT_EEERKNS0_10header_strEOT0_DpOT1_:
  404|      2|    {
  405|      2|        static_assert(!std::is_same_v<header_str, std::decay_t<V>>,
  406|      2|                      "Found header_str as value; suggest using std::string to "
  407|      2|                      "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|      2|        {
  418|      2|            apply_done(std::tuple_cat(std::move(ts),
  419|      2|                                      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|      2|    }
_ZN3lg27details14log_conversion10apply_doneINSt3__15tupleIJRNS_5levelERKNS3_15source_locationERPKcSB_mdSB_mlSB_mdSB_mlEEEEEvRKT_:
  354|      2|    {
  355|      2|        auto squash_string = [](auto& arg) -> decltype(auto) {
  356|      2|            if constexpr (std::is_same_v<const std::string&, decltype(arg)>)
  357|      2|            {
  358|      2|                return arg.data();
  359|      2|            }
  360|      2|            else
  361|      2|            {
  362|      2|                return arg;
  363|      2|            }
  364|      2|        };
  365|       |
  366|      2|        std::apply([squash_string](
  367|      2|                       const auto&... args) { done(squash_string(args)...); },
  368|      2|                   args_tuple);
  369|      2|    }
_ZZN3lg27details14log_conversion10apply_doneINSt3__15tupleIJRNS_5levelERKNS3_15source_locationERPKcSB_mdSB_mlSB_mdSB_mlEEEEEvRKT_ENKUlDpRKT_E_clIJS5_S7_SB_SB_mdSB_mlSB_mdSB_mlEEEDaSK_:
  367|      2|                       const auto&... args) { done(squash_string(args)...); },
_ZN3lg27details14log_conversion4doneIJRKPKcRKmRKdS6_S8_RKlS6_S8_SA_S6_S8_SC_EEEvNS_5levelERKNSt3__115source_locationES4_DpOT_:
  341|      2|    {
  342|      2|        do_log(l, s, m, ts..., nullptr);
  343|      2|    }
_ZZN3lg27details14log_conversion10apply_doneINSt3__15tupleIJRNS_5levelERKNS3_15source_locationERPKcSB_mdSB_mlSB_mdSB_mlEEEEEvRKT_ENKUlRSE_E_clIKS5_EEDcSH_:
  355|      2|        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|      2|            {
  362|      2|                return arg;
  363|      2|            }
  364|      2|        };
_ZZN3lg27details14log_conversion10apply_doneINSt3__15tupleIJRNS_5levelERKNS3_15source_locationERPKcSB_mdSB_mlSB_mdSB_mlEEEEEvRKT_ENKUlRSE_E_clIS8_EEDcSH_:
  355|      2|        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|      2|            {
  362|      2|                return arg;
  363|      2|            }
  364|      2|        };
_ZZN3lg27details14log_conversion10apply_doneINSt3__15tupleIJRNS_5levelERKNS3_15source_locationERPKcSB_mdSB_mlSB_mdSB_mlEEEEEvRKT_ENKUlRSE_E_clIKSB_EEDcSH_:
  355|     10|        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|     10|            {
  362|     10|                return arg;
  363|     10|            }
  364|     10|        };
_ZZN3lg27details14log_conversion10apply_doneINSt3__15tupleIJRNS_5levelERKNS3_15source_locationERPKcSB_mdSB_mlSB_mdSB_mlEEEEEvRKT_ENKUlRSE_E_clIKmEEDcSH_:
  355|      8|        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|      8|            {
  362|      8|                return arg;
  363|      8|            }
  364|      8|        };
_ZZN3lg27details14log_conversion10apply_doneINSt3__15tupleIJRNS_5levelERKNS3_15source_locationERPKcSB_mdSB_mlSB_mdSB_mlEEEEEvRKT_ENKUlRSE_E_clIKdEEDcSH_:
  355|      4|        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|      4|            {
  362|      4|                return arg;
  363|      4|            }
  364|      4|        };
_ZZN3lg27details14log_conversion10apply_doneINSt3__15tupleIJRNS_5levelERKNS3_15source_locationERPKcSB_mdSB_mlSB_mdSB_mlEEEEEvRKT_ENKUlRSE_E_clIKlEEDcSH_:
  355|      4|        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|      4|            {
  362|      4|                return arg;
  363|      4|            }
  364|      4|        };

_ZN3lg27details8prohibitITpTkNS0_9log_flagsEJETkNS0_9log_flagsENS0_13flag_floatingEEEvNS0_8log_flagIJDpT_EEENS3_IJT0_EEE:
   38|     12|{
   39|     12|    static_assert(!(... || std::is_same_v<Fs, F>),
   40|     12|                  "Prohibited flag found for value type.");
   41|     12|}
_ZN3lg27details8prohibitITpTkNS0_9log_flagsEJETkNS0_9log_flagsENS0_8flag_strEEEvNS0_8log_flagIJDpT_EEENS3_IJT0_EEE:
   38|     24|{
   39|     24|    static_assert(!(... || std::is_same_v<Fs, F>),
   40|     24|                  "Prohibited flag found for value type.");
   41|     24|}
_ZN3lg27details8prohibitITpTkNS0_9log_flagsEJETkNS0_9log_flagsENS0_17flag_unsigned_valEEEvNS0_8log_flagIJDpT_EEENS3_IJT0_EEE:
   38|     24|{
   39|     24|    static_assert(!(... || std::is_same_v<Fs, F>),
   40|     24|                  "Prohibited flag found for value type.");
   41|     24|}
_ZN3lg27details12one_from_setITpTkNS0_9log_flagsEJETpTkNS0_9log_flagsEJNS0_8flag_decENS0_8flag_hexENS0_8flag_binEEEEvNS0_8log_flagIJDpT_EEENS5_IJDpT0_EEE:
   47|     12|{
   48|     12|    static_assert(std::popcount(a.value & b.value) < 2,
   49|     12|                  "Conflicting flags found for value type.");
   50|     12|}
_ZN3lg27detailsorITpTkNS0_9log_flagsEJNS0_8flag_decENS0_8flag_hexEETpTkNS0_9log_flagsEJNS0_8flag_binEEEEDaNS0_8log_flagIJDpT_EEENS5_IJDpT0_EEE:
   31|     12|{
   32|     12|    return details::log_flag<As..., Bs...>{};
   33|     12|}
_ZN3lg27detailsorITpTkNS0_9log_flagsEJNS0_8flag_decEETpTkNS0_9log_flagsEJNS0_8flag_hexEEEEDaNS0_8log_flagIJDpT_EEENS4_IJDpT0_EEE:
   31|     12|{
   32|     12|    return details::log_flag<As..., Bs...>{};
   33|     12|}
_ZN3lg27details12one_from_setITpTkNS0_9log_flagsEJETpTkNS0_9log_flagsEJNS0_11flag_field8ENS0_12flag_field16ENS0_12flag_field32ENS0_12flag_field64EEEEvNS0_8log_flagIJDpT_EEENS6_IJDpT0_EEE:
   47|     12|{
   48|     12|    static_assert(std::popcount(a.value & b.value) < 2,
   49|     12|                  "Conflicting flags found for value type.");
   50|     12|}
_ZN3lg27detailsorITpTkNS0_9log_flagsEJNS0_11flag_field8ENS0_12flag_field16ENS0_12flag_field32EETpTkNS0_9log_flagsEJNS0_12flag_field64EEEEDaNS0_8log_flagIJDpT_EEENS6_IJDpT0_EEE:
   31|     12|{
   32|     12|    return details::log_flag<As..., Bs...>{};
   33|     12|}
_ZN3lg27detailsorITpTkNS0_9log_flagsEJNS0_11flag_field8ENS0_12flag_field16EETpTkNS0_9log_flagsEJNS0_12flag_field32EEEEDaNS0_8log_flagIJDpT_EEENS5_IJDpT0_EEE:
   31|     12|{
   32|     12|    return details::log_flag<As..., Bs...>{};
   33|     12|}
_ZN3lg27detailsorITpTkNS0_9log_flagsEJNS0_11flag_field8EETpTkNS0_9log_flagsEJNS0_12flag_field16EEEEDaNS0_8log_flagIJDpT_EEENS4_IJDpT0_EEE:
   31|     12|{
   32|     12|    return details::log_flag<As..., Bs...>{};
   33|     12|}
_ZN3lg27detailsorITpTkNS0_9log_flagsEJETpTkNS0_9log_flagsEJNS0_15flag_signed_valEEEEDaNS0_8log_flagIJDpT_EEENS3_IJDpT0_EEE:
   31|     12|{
   32|     12|    return details::log_flag<As..., Bs...>{};
   33|     12|}
_ZN3lg27details8prohibitITpTkNS0_9log_flagsEJETkNS0_9log_flagsENS0_8flag_binEEEvNS0_8log_flagIJDpT_EEENS3_IJT0_EEE:
   38|     12|{
   39|     12|    static_assert(!(... || std::is_same_v<Fs, F>),
   40|     12|                  "Prohibited flag found for value type.");
   41|     12|}
_ZN3lg27details8prohibitITpTkNS0_9log_flagsEJETkNS0_9log_flagsENS0_8flag_decEEEvNS0_8log_flagIJDpT_EEENS3_IJT0_EEE:
   38|     12|{
   39|     12|    static_assert(!(... || std::is_same_v<Fs, F>),
   40|     12|                  "Prohibited flag found for value type.");
   41|     12|}
_ZN3lg27details8prohibitITpTkNS0_9log_flagsEJETkNS0_9log_flagsENS0_12flag_field16EEEvNS0_8log_flagIJDpT_EEENS3_IJT0_EEE:
   38|     12|{
   39|     12|    static_assert(!(... || std::is_same_v<Fs, F>),
   40|     12|                  "Prohibited flag found for value type.");
   41|     12|}
_ZN3lg27details8prohibitITpTkNS0_9log_flagsEJETkNS0_9log_flagsENS0_12flag_field32EEEvNS0_8log_flagIJDpT_EEENS3_IJT0_EEE:
   38|     12|{
   39|     12|    static_assert(!(... || std::is_same_v<Fs, F>),
   40|     12|                  "Prohibited flag found for value type.");
   41|     12|}
_ZN3lg27details8prohibitITpTkNS0_9log_flagsEJETkNS0_9log_flagsENS0_12flag_field64EEEvNS0_8log_flagIJDpT_EEENS3_IJT0_EEE:
   38|     12|{
   39|     12|    static_assert(!(... || std::is_same_v<Fs, F>),
   40|     12|                  "Prohibited flag found for value type.");
   41|     12|}
_ZN3lg27details8prohibitITpTkNS0_9log_flagsEJETkNS0_9log_flagsENS0_11flag_field8EEEvNS0_8log_flagIJDpT_EEENS3_IJT0_EEE:
   38|     12|{
   39|     12|    static_assert(!(... || std::is_same_v<Fs, F>),
   40|     12|                  "Prohibited flag found for value type.");
   41|     12|}
_ZN3lg27details8prohibitITpTkNS0_9log_flagsEJETkNS0_9log_flagsENS0_8flag_hexEEEvNS0_8log_flagIJDpT_EEENS3_IJT0_EEE:
   38|     12|{
   39|     12|    static_assert(!(... || std::is_same_v<Fs, F>),
   40|     12|                  "Prohibited flag found for value type.");
   41|     12|}
_ZN3lg27details8prohibitITpTkNS0_9log_flagsEJETkNS0_9log_flagsENS0_15flag_signed_valEEEvNS0_8log_flagIJDpT_EEENS3_IJT0_EEE:
   38|     12|{
   39|     12|    static_assert(!(... || std::is_same_v<Fs, F>),
   40|     12|                  "Prohibited flag found for value type.");
   41|     12|}
_ZN3lg27detailsorITpTkNS0_9log_flagsEJETpTkNS0_9log_flagsEJNS0_13flag_floatingEEEEDaNS0_8log_flagIJDpT_EEENS3_IJDpT0_EEE:
   31|     12|{
   32|     12|    return details::log_flag<As..., Bs...>{};
   33|     12|}

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

LLVMFuzzerTestOneInput:
   22|    116|{
   23|       |    // We need 3 doubles (24 bytes) minimum
   24|    116|    if (size < 24)
  ------------------
  |  Branch (24:9): [True: 8, False: 108]
  ------------------
   25|      8|    {
   26|      8|        return 0;
   27|      8|    }
   28|       |
   29|    108|    double max, min, value;
   30|    108|    std::memcpy(&max, data, sizeof(double));
   31|    108|    std::memcpy(&min, data + 8, sizeof(double));
   32|    108|    std::memcpy(&value, data + 16, sizeof(double));
   33|       |
   34|       |    // Test getSensorAttributes
   35|    108|    int16_t mValue = 0;
   36|    108|    int8_t rExp = 0;
   37|    108|    int16_t bValue = 0;
   38|    108|    int8_t bExp = 0;
   39|    108|    bool bSigned = false;
   40|       |
   41|    108|    bool result =
   42|    108|        ipmi::getSensorAttributes(max, min, mValue, rExp, bValue, bExp, bSigned);
   43|       |
   44|       |    // If getSensorAttributes succeeded, also test scaleIPMIValueFromDouble
   45|    108|    if (result && mValue != 0)
  ------------------
  |  Branch (45:9): [True: 91, False: 17]
  |  Branch (45:19): [True: 91, False: 0]
  ------------------
   46|     91|    {
   47|     91|        try
   48|     91|        {
   49|     91|            ipmi::scaleIPMIValueFromDouble(value, mValue, rExp, bValue, bExp,
   50|     91|                                           bSigned);
   51|     91|        }
   52|     91|        catch (const std::out_of_range&)
   53|     91|        {
   54|       |            // Expected for mValue == 0, but we check above
   55|      0|        }
   56|     91|    }
   57|       |
   58|       |    // Test getScaledIPMIValue directly
   59|    108|    try
   60|    108|    {
   61|    108|        ipmi::getScaledIPMIValue(value, max, min);
   62|    108|    }
   63|    108|    catch (const std::runtime_error&)
   64|    108|    {
   65|       |        // Expected for invalid sensor attributes
   66|     17|    }
   67|    108|    catch (const std::out_of_range&)
   68|    108|    {
   69|       |        // Expected for mValue == 0 conditions
   70|      0|    }
   71|       |
   72|    108|    return 0;
   73|    108|}

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

