2 * Copyright 2016-2017 Milian Wolff <mail@milianw.de>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 #include "callercalleemodel.h"
24 #include <KLocalizedString>
29 #include <QTextStream>
35 QString basename(const QString& path)
37 int idx = path.lastIndexOf(QLatin1Char('/'));
38 return path.mid(idx + 1);
42 CallerCalleeModel::CallerCalleeModel(QObject* parent)
43 : QAbstractTableModel(parent)
45 qRegisterMetaType<CallerCalleeRows>();
48 CallerCalleeModel::~CallerCalleeModel() = default;
50 QVariant CallerCalleeModel::headerData(int section, Qt::Orientation orientation, int role) const
52 if (orientation != Qt::Horizontal || section < 0 || section >= NUM_COLUMNS) {
55 if (role == Qt::InitialSortOrderRole) {
56 if (section == SelfAllocatedColumn || section == SelfAllocationsColumn || section == SelfPeakColumn
57 || section == SelfPeakInstancesColumn || section == SelfLeakedColumn || section == SelfTemporaryColumn || section == InclusiveAllocatedColumn
58 || section == InclusivePeakInstancesColumn || section == InclusiveAllocationsColumn || section == InclusivePeakColumn
59 || section == InclusiveLeakedColumn || section == InclusiveTemporaryColumn) {
60 return Qt::DescendingOrder;
63 if (role == Qt::DisplayRole) {
64 switch (static_cast<Columns>(section)) {
70 return i18n("Function");
72 return i18n("Module");
73 case SelfAllocationsColumn:
74 return i18n("Allocations (Self)");
75 case SelfTemporaryColumn:
76 return i18n("Temporary (Self)");
78 return i18n("Peak (Self)");
79 case SelfPeakInstancesColumn:
80 return i18n("Peak instances (Self.)");
81 case SelfLeakedColumn:
82 return i18n("Leaked (Self)");
83 case SelfAllocatedColumn:
84 return i18n("Allocated (Self)");
85 case InclusiveAllocationsColumn:
86 return i18n("Allocations (Incl.)");
87 case InclusiveTemporaryColumn:
88 return i18n("Temporary (Incl.)");
89 case InclusivePeakColumn:
90 return i18n("Peak (Incl.)");
91 case InclusivePeakInstancesColumn:
92 return i18n("Peak instances (Incl.)");
93 case InclusiveLeakedColumn:
94 return i18n("Leaked (Incl.)");
95 case InclusiveAllocatedColumn:
96 return i18n("Allocated (Incl.)");
98 return i18n("Location");
102 } else if (role == Qt::ToolTipRole) {
103 switch (static_cast<Columns>(section)) {
105 return i18n("<qt>The file where the allocation function was called from. "
106 "May be empty when debug information is missing.</qt>");
108 return i18n("<qt>The line number where the allocation function was called from. "
109 "May be empty when debug information is missing.</qt>");
111 return i18n("<qt>The parent function that called an allocation function. "
112 "May be unknown when debug information is missing.</qt>");
114 return i18n("<qt>The module, i.e. executable or shared library, from "
115 "which an allocation function was "
117 case SelfAllocationsColumn:
118 return i18n("<qt>The number of times an allocation function was directly "
119 "called from this location.</qt>");
120 case SelfTemporaryColumn:
121 return i18n("<qt>The number of direct temporary allocations. These "
122 "allocations are directly followed by a "
123 "free without any other allocations in-between.</qt>");
125 return i18n("<qt>The maximum heap memory in bytes consumed from "
126 "allocations originating directly at "
128 "This takes deallocations into account.</qt>");
129 case SelfPeakInstancesColumn:
130 return i18n("<qt>The maximum number of instances in created"
131 "from allocations originating at this "
132 "location or from functions called from here. "
133 "This takes deallocations into account.</qt>");
134 case SelfLeakedColumn:
135 return i18n("<qt>The bytes allocated directly at this location that have "
136 "not been deallocated.</qt>");
137 case SelfAllocatedColumn:
138 return i18n("<qt>The sum of all bytes directly allocated from this "
139 "location, ignoring deallocations.</qt>");
140 case InclusiveAllocationsColumn:
141 return i18n("<qt>The inclusive number of times an allocation function "
142 "was called from this location or any "
143 "functions called from here.</qt>");
144 case InclusiveTemporaryColumn:
145 return i18n("<qt>The number of inclusive temporary allocations. These "
146 "allocations are directly followed by "
147 "a free without any other allocations in-between.</qt>");
148 case InclusivePeakColumn:
149 return i18n("<qt>The inclusive maximum heap memory in bytes consumed "
150 "from allocations originating at this "
151 "location or from functions called from here. "
152 "This takes deallocations into account.</qt>");
153 case InclusivePeakInstancesColumn:
154 return i18n("<qt>The inclusive maximum number of instances in created"
155 "from allocations originating at this "
156 "location or from functions called from here. "
157 "This takes deallocations into account.</qt>");
158 case InclusiveLeakedColumn:
159 return i18n("<qt>The bytes allocated at this location that have not been "
160 "deallocated.</qt>");
161 case InclusiveAllocatedColumn:
162 return i18n("<qt>The inclusive sum of all bytes allocated from this "
163 "location or functions called from "
164 "here, ignoring deallocations.</qt>");
166 return i18n("<qt>The location from which an allocation function was "
167 "called. Function symbol and file "
169 "may be unknown when debug information was missing when "
170 "heaptrack was run.</qt>");
178 QVariant CallerCalleeModel::data(const QModelIndex& index, int role) const
180 if (!hasIndex(index.row(), index.column(), index.parent())) {
184 const auto& row = (role == MaxCostRole) ? m_maxCost : m_rows.at(index.row());
186 if (role == Qt::DisplayRole || role == SortRole || role == MaxCostRole) {
187 switch (static_cast<Columns>(index.column())) {
188 case SelfAllocatedColumn:
189 if (role == SortRole || role == MaxCostRole) {
190 return static_cast<qint64>(row.selfCost.allocated);
192 return Util::formatByteSize(row.selfCost.allocated, 1);
194 case SelfAllocationsColumn:
195 return static_cast<qint64>(row.selfCost.allocations);
196 case SelfTemporaryColumn:
197 return static_cast<qint64>(row.selfCost.temporary);
199 if (role == SortRole || role == MaxCostRole) {
200 return static_cast<qint64>(row.selfCost.peak);
202 return Util::formatByteSize(row.selfCost.peak, 1);
204 case SelfPeakInstancesColumn:
205 return static_cast<qint64>(row.selfCost.peak_instances);
206 case SelfLeakedColumn:
207 if (role == SortRole || role == MaxCostRole) {
208 return static_cast<qint64>(row.selfCost.leaked);
210 return Util::formatByteSize(row.selfCost.leaked, 1);
212 case InclusiveAllocatedColumn:
213 if (role == SortRole || role == MaxCostRole) {
214 return static_cast<qint64>(row.inclusiveCost.allocated);
216 return Util::formatByteSize(row.inclusiveCost.allocated, 1);
218 case InclusiveAllocationsColumn:
219 return static_cast<qint64>(row.inclusiveCost.allocations);
220 case InclusiveTemporaryColumn:
221 return static_cast<qint64>(row.inclusiveCost.temporary);
222 case InclusivePeakColumn:
223 if (role == SortRole || role == MaxCostRole) {
224 return static_cast<qint64>(row.inclusiveCost.peak);
226 return Util::formatByteSize(row.inclusiveCost.peak, 1);
228 case InclusivePeakInstancesColumn:
229 return static_cast<qint64>(row.inclusiveCost.peak_instances);
230 case InclusiveLeakedColumn:
231 if (role == SortRole || role == MaxCostRole) {
232 return static_cast<qint64>(row.inclusiveCost.leaked);
234 return Util::formatByteSize(row.inclusiveCost.leaked, 1);
237 return row.location->function;
239 return row.location->module;
241 return row.location->file;
243 return row.location->line;
245 if (row.location->file.isEmpty()) {
246 return i18n("%1 in ?? (%2)", basename(row.location->function), basename(row.location->module));
248 return i18n("%1 in %2:%3 (%4)", row.location->function, basename(row.location->file),
249 row.location->line, basename(row.location->module));
254 } else if (role == Qt::ToolTipRole) {
256 QTextStream stream(&tooltip);
257 stream << "<qt><pre style='font-family:monospace;'>";
258 if (row.location->line > 0) {
259 stream << i18nc("1: function, 2: file, 3: line, 4: module", "%1\n at %2:%3\n in %4",
260 row.location->function.toHtmlEscaped(), row.location->file.toHtmlEscaped(),
261 row.location->line, row.location->module.toHtmlEscaped());
263 stream << i18nc("1: function, 2: module", "%1\n in %2", row.location->function.toHtmlEscaped(),
264 row.location->module.toHtmlEscaped());
267 stream << i18n("inclusive: allocated %1 over %2 calls (%3 temporary, i.e. "
268 "%4%), peak at %5, leaked %6",
269 Util::formatByteSize(row.inclusiveCost.allocated, 1),
270 row.inclusiveCost.allocations,
271 row.inclusiveCost.temporary, round(float(row.inclusiveCost.temporary) * 100.f * 100.f
272 / std::max(int64_t(1), row.inclusiveCost.allocations))
274 Util::formatByteSize(row.inclusiveCost.peak, 1),
275 Util::formatByteSize(row.inclusiveCost.leaked, 1));
278 "self: allocated %1 over %2 calls (%3 temporary, i.e. %4%), "
279 "peak at %5, leaked %6",
280 Util::formatByteSize(row.selfCost.allocated, 1),
281 row.selfCost.allocations, row.selfCost.temporary,
282 round(float(row.selfCost.temporary) * 100.f * 100.f / std::max(int64_t(1), row.selfCost.allocations))
284 Util::formatByteSize(row.selfCost.peak, 1),
285 Util::formatByteSize(row.selfCost.leaked, 1));
287 stream << "</pre></qt>";
289 } else if (role == LocationRole) {
290 return QVariant::fromValue(row.location);
295 int CallerCalleeModel::columnCount(const QModelIndex& parent) const
297 return parent.isValid() ? 0 : NUM_COLUMNS;
300 int CallerCalleeModel::rowCount(const QModelIndex& parent) const
302 return parent.isValid() ? 0 : m_rows.size();
305 void CallerCalleeModel::resetData(const QVector<CallerCalleeData>& rows)
312 void CallerCalleeModel::setSummary(const SummaryData& data)
315 m_maxCost.inclusiveCost = data.cost;
316 m_maxCost.selfCost = data.cost;
320 void CallerCalleeModel::clearData()