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/> %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/> %4% out of %3 total", label, selfCost, total, 0135 Util::formatCostRelative(selfCost, total)) 0136 + QLatin1String("<br/>") 0137 + i18n("%1 (inclusive): %2<br/> %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/> %4% out of %3 total", label, selfCost, total, 0164 Util::formatCostRelative(selfCost, total)) 0165 + QLatin1String("<br/>") 0166 + i18n("%1 (inclusive): %2<br/> %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 }