Simplifies the code in some areas.
const bool reparsing = totalTime != 0;
m_maxAllocationTraceIndex.index = 0;
- totalAllocated = 0;
- totalAllocations = 0;
- totalTemporary = 0;
- peak = 0;
+ totalCost = {};
peakTime = 0;
- leaked = 0;
systemInfo = {};
peakRSS = 0;
allocations.clear();
allocation.peak = allocation.leaked;
}
- totalAllocated += info.size;
- ++totalAllocations;
- leaked += info.size;
- if (leaked > peak) {
- peak = leaked;
+ ++totalCost.allocations;
+ totalCost.allocated += info.size;
+ totalCost.leaked += info.size;
+ if (totalCost.leaked > totalCost.peak) {
+ totalCost.peak = totalCost.leaked;
peakTime = timeStamp;
}
} else {
allocation.leaked -= info.size;
}
- leaked -= info.size;
+ totalCost.leaked -= info.size;
if (temporary) {
++allocation.temporary;
- ++totalTemporary;
+ ++totalCost.temporary;
}
} else if (reader.mode() == 'a') {
if (reparsing) {
} else if (reader.mode() == 'X') {
handleDebuggee(reader.line().c_str() + 2);
} else if (reader.mode() == 'A') {
- leaked = 0;
- peak = 0;
+ totalCost = {};
fromAttached = true;
} else if (reader.mode() == 'v') {
reader >> fileVersion;
#include <boost/functional/hash.hpp>
#include "indices.h"
+#include "allocationdata.h"
struct InstructionPointer
{
TraceIndex parentIndex;
};
-struct AllocationData
-{
- // number of allocations
- uint64_t allocations = 0;
- // bytes allocated in total
- uint64_t allocated = 0;
- // amount of bytes leaked
- uint64_t leaked = 0;
- // largest amount of bytes allocated
- uint64_t peak = 0;
- // number of temporary allocations
- uint64_t temporary = 0;
-};
-
struct Allocation : public AllocationData
{
// backtrace entry point
bool fromAttached = false;
std::vector<Allocation> allocations;
- uint64_t totalAllocated = 0;
- uint64_t totalAllocations = 0;
- uint64_t totalTemporary = 0;
- uint64_t peak = 0;
- uint64_t leaked = 0;
+ AllocationData totalCost;
uint64_t totalTime = 0;
uint64_t peakTime = 0;
uint64_t peakRSS = 0;
--- /dev/null
+/*
+ * Copyright 2016 Milian Wolff <mail@milianw.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef ALLOCATIONDATA_H
+#define ALLOCATIONDATA_H
+
+#include <cstdint>
+
+struct AllocationData
+{
+ // number of allocations
+ uint64_t allocations = 0;
+ // number of temporary allocations
+ uint64_t temporary = 0;
+ // bytes allocated in total
+ uint64_t allocated = 0;
+ // amount of bytes leaked
+ uint64_t leaked = 0;
+ // largest amount of bytes allocated
+ uint64_t peak = 0;
+};
+
+inline AllocationData& operator+=(AllocationData& lhs, const AllocationData& rhs)
+{
+ lhs.allocations += rhs.allocations;
+ lhs.temporary += rhs.temporary;
+ lhs.peak += rhs.peak;
+ lhs.leaked += rhs.leaked;
+ lhs.allocated += rhs.allocated;
+ return lhs;
+}
+
+inline AllocationData operator+(AllocationData lhs, const AllocationData& rhs)
+{
+ return lhs += rhs;
+}
+
+#endif // ALLOCATIONDATA_H
/**
* Convert the top-down graph into a tree of FrameGraphicsItem.
*/
-void toGraphicsItems(const QVector<RowData>& data, FrameGraphicsItem *parent, quint64 RowData::* member)
+void toGraphicsItems(const QVector<RowData>& data, FrameGraphicsItem *parent, uint64_t AllocationData::* member)
{
foreach (const auto& row, data) {
auto item = findItemByFunction(parent->childItems(), row.location->function);
if (!item) {
- item = new FrameGraphicsItem(row.*member, row.location->function, parent);
+ item = new FrameGraphicsItem(row.cost.*member, row.location->function, parent);
item->setPen(parent->pen());
item->setBrush(brush());
} else {
- item->setCost(item->cost() + row.*member);
+ item->setCost(item->cost() + row.cost.*member);
}
toGraphicsItems(row.children, item, member);
}
}
-quint64 RowData::* memberForType(CostType type)
+uint64_t AllocationData::* memberForType(CostType type)
{
switch (type) {
case Allocations:
- return &RowData::allocations;
+ return &AllocationData::allocations;
case Temporary:
- return &RowData::temporary;
+ return &AllocationData::temporary;
case Peak:
- return &RowData::peak;
+ return &AllocationData::peak;
case Leaked:
- return &RowData::leaked;
+ return &AllocationData::leaked;
case Allocated:
- return &RowData::allocated;
+ return &AllocationData::allocated;
}
Q_UNREACHABLE();
}
FrameGraphicsItem* parseData(const QVector<RowData>& topDownData, CostType type)
{
- quint64 RowData::* member = memberForType(type);
+ uint64_t AllocationData::* member = memberForType(type);
double totalCost = 0;
foreach(const auto& frame, topDownData) {
- totalCost += frame.*member;
+ totalCost += frame.cost.*member;
}
KColorScheme scheme(QPalette::Active);
QTextStream stream(&textCenter);
stream << "<qt><dl>"
<< i18n("<dt><b>calls to allocation functions</b>:</dt><dd>%1 (%2/s)</dd>",
- data.allocations, quint64(data.allocations / totalTimeS))
+ data.cost.allocations, quint64(data.cost.allocations / totalTimeS))
<< i18n("<dt><b>temporary allocations</b>:</dt><dd>%1 (%2%, %3/s)</dd>",
- data.temporary, round(float(data.temporary) * 100.f * 100.f / data.allocations) / 100.f,
- quint64(data.temporary / totalTimeS))
+ data.cost.temporary, round(float(data.cost.temporary) * 100.f * 100.f / data.cost.allocations) / 100.f,
+ quint64(data.cost.temporary / totalTimeS))
<< i18n("<dt><b>bytes allocated in total</b> (ignoring deallocations):</dt><dd>%1 (%2/s)</dd>",
- format.formatByteSize(data.allocated, 2), format.formatByteSize(data.allocated / totalTimeS))
+ format.formatByteSize(data.cost.allocated, 2), format.formatByteSize(data.cost.allocated / totalTimeS))
<< "</dl></qt>";
}
{
QTextStream stream(&textRight);
stream << "<qt><dl>"
- << i18n("<dt><b>peak heap memory consumption</b>:</dt><dd>%1 after %2s</dd>", format.formatByteSize(data.peak), peakTimeS)
+ << i18n("<dt><b>peak heap memory consumption</b>:</dt><dd>%1 after %2s</dd>", format.formatByteSize(data.cost.peak), peakTimeS)
<< i18n("<dt><b>peak RSS</b> (including heaptrack overhead):</dt><dd>%1</dd>", format.formatByteSize(data.peakRSS))
- << i18n("<dt><b>total memory leaked</b>:</dt><dd>%1</dd>", format.formatByteSize(data.leaked))
+ << i18n("<dt><b>total memory leaked</b>:</dt><dd>%1</dd>", format.formatByteSize(data.cost.leaked))
<< "</dl></qt>";
}
if (!buildCharts) {
return;
}
- maxConsumedSinceLastTimeStamp = max(maxConsumedSinceLastTimeStamp, leaked);
+ maxConsumedSinceLastTimeStamp = max(maxConsumedSinceLastTimeStamp, totalCost.leaked);
const uint64_t diffBetweenTimeStamps = totalTime / MAX_CHART_DATAPOINTS;
if (newStamp != totalTime && newStamp - lastTimeStamp < diffBetweenTimeStamps) {
return;
return row;
};
auto consumed = createRow(newStamp, nowConsumed);
- auto allocated = createRow(newStamp, totalAllocated);
- auto allocs = createRow(newStamp, totalAllocations);
- auto temporary = createRow(newStamp, totalTemporary);
+ auto allocated = createRow(newStamp, totalCost.allocated);
+ auto allocs = createRow(newStamp, totalCost.allocations);
+ auto temporary = createRow(newStamp, totalCost.temporary);
// if the cost is non-zero and the ip corresponds to a hotspot function selected in the labels,
// we add the cost to the rows column
void handleAllocation(const AllocationInfo& info, const AllocationIndex index)
{
- maxConsumedSinceLastTimeStamp = max(maxConsumedSinceLastTimeStamp, leaked);
+ maxConsumedSinceLastTimeStamp = max(maxConsumedSinceLastTimeStamp, totalCost.leaked);
if (index.index == allocationInfoCounter.size()) {
allocationInfoCounter.push_back({info, 1});
auto location = data.stringCache.location(ip);
auto it = lower_bound(rows->begin(), rows->end(), location);
if (it != rows->end() && it->location == location) {
- it->allocated += allocation.allocated;
- it->allocations += allocation.allocations;
- it->temporary += allocation.temporary;
- it->leaked += allocation.leaked;
- it->peak += allocation.peak;
+ it->cost += allocation;
} else {
- it = rows->insert(it, {allocation.allocations, allocation.peak, allocation.leaked, allocation.allocated, allocation.temporary,
- location, nullptr, {}});
+ it = rows->insert(it, {allocation, location, nullptr, {}});
}
if (data.isStopIndex(ip.functionIndex)) {
break;
auto data = findByLocation(*node, stack);
if (!data) {
// create an empty top-down item for this bottom-up node
- *stack << RowData{0, 0, 0, 0, 0, node->location, nullptr, {}};
+ *stack << RowData{{}, node->location, nullptr, {}};
data = &stack->back();
}
// always use the leaf node's cost and propagate that one up the chain
// otherwise we'd count the cost of some nodes multiple times
- data->allocations += row.allocations;
- data->peak += row.peak;
- data->leaked += row.leaked;
- data->allocated += row.allocated;
- data->temporary += row.temporary;
+ data->cost += row.cost;
stack = &data->children;
node = node->parent;
}
emit summaryAvailable({
QString::fromStdString(data->debuggee),
+ data->totalCost,
data->totalTime,
data->peakTime,
- data->peak,
- data->leaked,
- data->totalAllocations,
- data->totalTemporary,
- data->totalAllocated,
data->peakRSS * data->systemInfo.pageSize,
data->systemInfo.pages * data->systemInfo.pageSize
});
switch (static_cast<Columns>(index.column())) {
case AllocatedColumn:
if (role == SortRole || role == MaxCostRole) {
- return row->allocated;
+ return static_cast<quint64>(row->cost.allocated);
} else {
- return m_format.formatByteSize(row->allocated);
+ return m_format.formatByteSize(row->cost.allocated);
}
case AllocationsColumn:
- return row->allocations;
+ return static_cast<quint64>(row->cost.allocations);
case TemporaryColumn:
- return row->temporary;
+ return static_cast<quint64>(row->cost.temporary);
case PeakColumn:
if (role == SortRole || role == MaxCostRole) {
- return row->peak;
+ return static_cast<quint64>(row->cost.peak);
} else {
- return m_format.formatByteSize(row->peak);
+ return m_format.formatByteSize(row->cost.peak);
}
case LeakedColumn:
if (role == SortRole || role == MaxCostRole) {
- return row->leaked;
+ return static_cast<quint64>(row->cost.leaked);
} else {
- return m_format.formatByteSize(row->leaked);
+ return m_format.formatByteSize(row->cost.leaked);
}
case FunctionColumn:
return row->location->function;
stream << '\n';
KFormat format;
stream << i18n("allocated %1 over %2 calls (%3 temporary, i.e. %4%), peak at %5, leaked %6",
- format.formatByteSize(row->allocated), row->allocations, row->temporary,
- round(float(row->temporary) * 100.f * 100.f / row->allocations) / 100.f,
- format.formatByteSize(row->peak), format.formatByteSize(row->leaked));
+ format.formatByteSize(row->cost.allocated), row->cost.allocations, row->cost.temporary,
+ round(float(row->cost.temporary) * 100.f * 100.f / row->cost.allocations) / 100.f,
+ format.formatByteSize(row->cost.peak), format.formatByteSize(row->cost.leaked));
stream << '\n';
if (!row->children.isEmpty()) {
auto child = row;
void TreeModel::setSummary(const SummaryData& data)
{
beginResetModel();
- m_maxCost.allocated = data.allocated;
- m_maxCost.leaked = data.leaked;
- m_maxCost.peak = data.peak;
- m_maxCost.allocations = data.allocations;
- m_maxCost.temporary = data.temporary;
+ m_maxCost.cost = data.cost;
endResetModel();
}
#include <KFormat>
-#include <boost/functional/hash.hpp>
-
#include <memory>
+#include "../allocationdata.h"
+
struct SummaryData
{
QString debuggee;
+ AllocationData cost;
uint64_t totalTime;
uint64_t peakTime;
- uint64_t peak;
- uint64_t leaked;
- uint64_t allocations;
- uint64_t temporary;
- uint64_t allocated;
uint64_t peakRSS;
uint64_t totalSystemMemory;
};
struct RowData
{
- quint64 allocations;
- quint64 peak;
- quint64 leaked;
- quint64 allocated;
- quint64 temporary;
+ AllocationData cost;
std::shared_ptr<LocationData> location;
const RowData* parent;
QVector<RowData> children;
void writeMassifSnapshot(size_t timeStamp, bool isLast)
{
if (!lastMassifPeak) {
- lastMassifPeak = leaked;
+ lastMassifPeak = totalCost.leaked;
massifAllocations = allocations;
}
massifOut
++sizeHistogram[info.size];
}
- if (leaked > lastMassifPeak && massifOut.is_open()) {
+ if (totalCost.leaked > lastMassifPeak && massifOut.is_open()) {
massifAllocations = allocations;
- lastMassifPeak = leaked;
+ lastMassifPeak = totalCost.leaked;
}
}
const double totalTimeS = 0.001 * data.totalTime;
cout << "total runtime: " << fixed << totalTimeS << "s.\n"
- << "bytes allocated in total (ignoring deallocations): " << formatBytes(data.totalAllocated)
- << " (" << formatBytes(data.totalAllocated / totalTimeS) << "/s)" << '\n'
- << "calls to allocation functions: " << data.totalAllocations
- << " (" << size_t(data.totalAllocations / totalTimeS) << "/s)\n"
- << "temporary memory allocations: " << data.totalTemporary
- << " (" << size_t(data.totalTemporary / totalTimeS) << "/s)\n"
- << "peak heap memory consumption: " << formatBytes(data.peak) << '\n'
+ << "bytes allocated in total (ignoring deallocations): " << formatBytes(data.totalCost.allocated)
+ << " (" << formatBytes(data.totalCost.allocated / totalTimeS) << "/s)" << '\n'
+ << "calls to allocation functions: " << data.totalCost.allocations
+ << " (" << size_t(data.totalCost.allocations / totalTimeS) << "/s)\n"
+ << "temporary memory allocations: " << data.totalCost.temporary
+ << " (" << size_t(data.totalCost.temporary / totalTimeS) << "/s)\n"
+ << "peak heap memory consumption: " << formatBytes(data.totalCost.peak) << '\n'
<< "peak RSS (including heaptrack overhead): " << formatBytes(data.peakRSS * data.systemInfo.pageSize) << '\n'
- << "total memory leaked: " << formatBytes(data.leaked) << '\n';
+ << "total memory leaked: " << formatBytes(data.totalCost.leaked) << '\n';
if (!printHistogram.empty()) {
ofstream histogram(printHistogram, ios_base::out);