File indexing completed on 2024-05-19 05:44:26

0001 /*
0002     SPDX-FileCopyrightText: 2017-2019 Milian Wolff <mail@milianw.de>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-or-later
0005 */
0006 
0007 #include "util.h"
0008 
0009 #include <QString>
0010 
0011 #include <resultdata.h>
0012 
0013 #include <KFormat>
0014 #include <KLocalizedString>
0015 
0016 namespace {
0017 const KFormat& format()
0018 {
0019     static const KFormat format;
0020     return format;
0021 }
0022 }
0023 
0024 QString Util::basename(const QString& path)
0025 {
0026     int idx = path.lastIndexOf(QLatin1Char('/'));
0027     return path.mid(idx + 1);
0028 }
0029 
0030 QString Util::formatString(const QString& input)
0031 {
0032     return input.isEmpty() ? i18n("??") : input;
0033 }
0034 
0035 QString Util::formatTime(qint64 ms)
0036 {
0037     auto format = [](quint64 fragment, int precision) -> QString {
0038         return QString::number(fragment).rightJustified(precision, QLatin1Char('0'));
0039     };
0040 
0041     if (std::abs(ms) < 1000) {
0042         QString ret = QString::number(ms) + QLatin1String("ms");
0043     }
0044 
0045     const auto isNegative = ms < 0;
0046     if (isNegative)
0047         ms = -ms;
0048     qint64 totalSeconds = ms / 1000;
0049     ms = ms % 1000;
0050     qint64 days = totalSeconds / 60 / 60 / 24;
0051     qint64 hours = (totalSeconds / 60 / 60) % 24;
0052     qint64 minutes = (totalSeconds / 60) % 60;
0053     qint64 seconds = totalSeconds % 60;
0054 
0055     auto optional = [](quint64 fragment, const char* unit) -> QString {
0056         if (fragment > 0)
0057             return QString::number(fragment) + QLatin1String(unit);
0058         return QString();
0059     };
0060 
0061     QString ret = optional(days, "d") + optional(hours, "h") + optional(minutes, "min");
0062     const auto showMs = ret.isEmpty();
0063     ret += format(seconds, 2);
0064     if (showMs)
0065         ret += QLatin1Char('.') + format(showMs ? ms : 0, 3);
0066     ret += QLatin1Char('s');
0067     if (isNegative)
0068         ret.prepend(QLatin1Char('-'));
0069     return ret;
0070 }
0071 
0072 QString Util::formatBytes(qint64 bytes)
0073 {
0074     auto ret = format().formatByteSize(bytes, 1, KFormat::MetricBinaryDialect);
0075     // remove spaces, otherwise HTML might break between the unit and the cost
0076     // note that we also don't add a space before our time units above
0077     ret.remove(QLatin1Char(' '));
0078     return ret;
0079 }
0080 
0081 QString Util::formatCostRelative(qint64 selfCost, qint64 totalCost, bool addPercentSign)
0082 {
0083     if (!totalCost) {
0084         return QString();
0085     }
0086 
0087     auto ret = QString::number(static_cast<double>(selfCost) * 100. / totalCost, 'g', 3);
0088     if (addPercentSign) {
0089         ret.append(QLatin1Char('%'));
0090     }
0091     return ret;
0092 }
0093 
0094 QString Util::formatTooltip(const Symbol& symbol, const AllocationData& costs, const ResultData& resultData)
0095 {
0096     const auto& totalCosts = resultData.totalCosts();
0097 
0098     auto toolTip = Util::toString(symbol, resultData, Util::Long);
0099 
0100     auto formatCost = [&](const QString& label, int64_t AllocationData::*member) -> QString {
0101         const auto cost = costs.*member;
0102         const auto total = totalCosts.*member;
0103         if (!total) {
0104             return QString();
0105         }
0106 
0107         return QLatin1String("<hr/>")
0108             + i18n("%1: %2<br/>&nbsp;&nbsp;%4% out of %3 total", label, cost, total,
0109                    Util::formatCostRelative(cost, total));
0110     };
0111 
0112     toolTip += formatCost(i18n("Peak"), &AllocationData::peak);
0113     toolTip += formatCost(i18n("Leaked"), &AllocationData::leaked);
0114     toolTip += formatCost(i18n("Allocations"), &AllocationData::allocations);
0115     toolTip += formatCost(i18n("Temporary Allocations"), &AllocationData::temporary);
0116     return QString(QLatin1String("<qt>") + toolTip + QLatin1String("</qt>"));
0117 }
0118 
0119 QString Util::formatTooltip(const Symbol& symbol, const AllocationData& selfCosts, const AllocationData& inclusiveCosts,
0120                             const ResultData& resultData)
0121 {
0122     const auto& totalCosts = resultData.totalCosts();
0123     auto toolTip = Util::toString(symbol, resultData, Util::Long);
0124 
0125     auto formatCost = [&](const QString& label, int64_t AllocationData::*member) -> QString {
0126         const auto selfCost = selfCosts.*member;
0127         const auto inclusiveCost = inclusiveCosts.*member;
0128         const auto total = totalCosts.*member;
0129         if (!total) {
0130             return QString();
0131         }
0132 
0133         return QLatin1String("<hr/>")
0134             + i18n("%1 (self): %2<br/>&nbsp;&nbsp;%4% out of %3 total", label, selfCost, total,
0135                    Util::formatCostRelative(selfCost, total))
0136             + QLatin1String("<br/>")
0137             + i18n("%1 (inclusive): %2<br/>&nbsp;&nbsp;%4% out of %3 total", label, inclusiveCost, total,
0138                    Util::formatCostRelative(inclusiveCost, total));
0139     };
0140 
0141     toolTip += formatCost(i18n("Peak"), &AllocationData::peak);
0142     toolTip += formatCost(i18n("Leaked"), &AllocationData::leaked);
0143     toolTip += formatCost(i18n("Allocations"), &AllocationData::allocations);
0144     toolTip += formatCost(i18n("Temporary Allocations"), &AllocationData::temporary);
0145     return QString(QLatin1String("<qt>") + toolTip + QLatin1String("</qt>"));
0146 }
0147 
0148 QString Util::formatTooltip(const FileLine& location, const AllocationData& selfCosts,
0149                             const AllocationData& inclusiveCosts, const ResultData& resultData)
0150 {
0151     QString toolTip = toString(location, resultData, Util::Long).toHtmlEscaped();
0152     const auto& totalCosts = resultData.totalCosts();
0153 
0154     auto formatCost = [&](const QString& label, int64_t AllocationData::*member) -> QString {
0155         const auto selfCost = selfCosts.*member;
0156         const auto inclusiveCost = inclusiveCosts.*member;
0157         const auto total = totalCosts.*member;
0158         if (!total) {
0159             return QString();
0160         }
0161 
0162         return QLatin1String("<hr/>")
0163             + i18n("%1 (self): %2<br/>&nbsp;&nbsp;%4% out of %3 total", label, selfCost, total,
0164                    Util::formatCostRelative(selfCost, total))
0165             + QLatin1String("<br/>")
0166             + i18n("%1 (inclusive): %2<br/>&nbsp;&nbsp;%4% out of %3 total", label, inclusiveCost, total,
0167                    Util::formatCostRelative(inclusiveCost, total));
0168     };
0169 
0170     toolTip += formatCost(i18n("Peak"), &AllocationData::peak);
0171     toolTip += formatCost(i18n("Leaked"), &AllocationData::leaked);
0172     toolTip += formatCost(i18n("Allocations"), &AllocationData::allocations);
0173     toolTip += formatCost(i18n("Temporary Allocations"), &AllocationData::temporary);
0174     return QString(QLatin1String("<qt>") + toolTip + QLatin1String("</qt>"));
0175 }
0176 
0177 QString Util::toString(const Symbol& symbol, const ResultData& resultData, FormatType formatType)
0178 {
0179     const auto& binaryPath = resultData.string(symbol.moduleId);
0180     const auto binaryName = Util::basename(binaryPath);
0181     switch (formatType) {
0182     case Long:
0183         return i18n("symbol: <tt>%1</tt><br/>binary: <tt>%2 (%3)</tt>",
0184                     resultData.string(symbol.functionId).toHtmlEscaped(), binaryName.toHtmlEscaped(),
0185                     binaryPath.toHtmlEscaped());
0186     case Short:
0187         return i18nc("%1: function name, %2: binary basename", "%1 in %2", resultData.string(symbol.functionId),
0188                      Util::basename(resultData.string(symbol.moduleId)));
0189     }
0190     Q_UNREACHABLE();
0191 }
0192 
0193 QString Util::toString(const FileLine& location, const ResultData& resultData, FormatType formatType)
0194 {
0195     auto file = resultData.string(location.fileId);
0196     switch (formatType) {
0197     case Long:
0198         break;
0199     case Short:
0200         file = Util::basename(file);
0201         break;
0202     }
0203 
0204     return file.isEmpty() ? QStringLiteral("??") : (file + QLatin1Char(':') + QString::number(location.line));
0205 }
0206 
0207 const QString& Util::unresolvedFunctionName()
0208 {
0209     static QString msg = i18n("<unresolved function>");
0210     return msg;
0211 }