1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
|
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ATraceMemoryDump.h"
#include <utils/Trace.h>
#include <cstring>
namespace android {
namespace uirenderer {
namespace skiapipeline {
// When purgeable is INVALID_MEMORY_SIZE it won't be logged at all.
#define INVALID_MEMORY_SIZE -1
/**
* Skia invokes the following SkTraceMemoryDump functions:
* 1. dumpNumericValue (dumpName, units="bytes", valueName="size")
* 2. dumpStringValue (dumpName, valueName="type") [optional -> for example CPU memory does not
* invoke dumpStringValue]
* 3. dumpNumericValue (dumpName, units="bytes", valueName="purgeable_size") [optional]
* 4. setMemoryBacking(dumpName, backingType) [optional -> for example Vulkan GPU resources do not
* invoke setMemoryBacking]
*
* ATraceMemoryDump calculates memory category first by looking at the "type" string passed to
* dumpStringValue and then by looking at "backingType" passed to setMemoryBacking.
* Only GPU Texture memory is tracked separately and everything else is grouped as one
* "Misc Memory" category.
*/
static std::unordered_map<const char*, const char*> sResourceMap = {
{"malloc", "HWUI CPU Memory"}, // taken from setMemoryBacking(backingType)
{"gl_texture", "HWUI Texture Memory"}, // taken from setMemoryBacking(backingType)
{"Texture", "HWUI Texture Memory"}, // taken from dumpStringValue(value, valueName="type")
// Uncomment categories below to split "Misc Memory" into more brackets for debugging.
/*{"vk_buffer", "vk_buffer"},
{"gl_renderbuffer", "gl_renderbuffer"},
{"gl_buffer", "gl_buffer"},
{"RenderTarget", "RenderTarget"},
{"Stencil", "Stencil"},
{"Path Data", "Path Data"},
{"Buffer Object", "Buffer Object"},
{"Surface", "Surface"},*/
};
ATraceMemoryDump::ATraceMemoryDump() {
mLastDumpName.reserve(100);
mCategory.reserve(100);
}
void ATraceMemoryDump::dumpNumericValue(const char* dumpName, const char* valueName,
const char* units, uint64_t value) {
if (!strcmp(units, "bytes")) {
recordAndResetCountersIfNeeded(dumpName);
if (!strcmp(valueName, "size")) {
mLastDumpValue = value;
} else if (!strcmp(valueName, "purgeable_size")) {
mLastPurgeableDumpValue = value;
}
}
}
void ATraceMemoryDump::dumpStringValue(const char* dumpName, const char* valueName,
const char* value) {
if (!strcmp(valueName, "type")) {
recordAndResetCountersIfNeeded(dumpName);
auto categoryIt = sResourceMap.find(value);
if (categoryIt != sResourceMap.end()) {
mCategory = categoryIt->second;
}
}
}
void ATraceMemoryDump::setMemoryBacking(const char* dumpName, const char* backingType,
const char* backingObjectId) {
recordAndResetCountersIfNeeded(dumpName);
auto categoryIt = sResourceMap.find(backingType);
if (categoryIt != sResourceMap.end()) {
mCategory = categoryIt->second;
}
}
/**
* startFrame is invoked before dumping anything. It resets counters from the previous frame.
* This is important, because if there is no new data for a given category trace would assume
* usage has not changed (instead of reporting 0).
*/
void ATraceMemoryDump::startFrame() {
resetCurrentCounter("");
for (auto& it : mCurrentValues) {
// Once a category is observed in at least one frame, it is always reported in subsequent
// frames (even if it is 0). Not logging a category to ATRACE would mean its value has not
// changed since the previous frame, which is not what we want.
it.second.memory = 0;
// If purgeableMemory is INVALID_MEMORY_SIZE, then logTraces won't log it at all.
if (it.second.purgeableMemory != INVALID_MEMORY_SIZE) {
it.second.purgeableMemory = 0;
}
}
}
/**
* logTraces reads from mCurrentValues and logs the counters with ATRACE.
*/
void ATraceMemoryDump::logTraces() {
// Accumulate data from last dumpName
recordAndResetCountersIfNeeded("");
uint64_t hwui_all_frame_memory = 0;
for (auto& it : mCurrentValues) {
hwui_all_frame_memory += it.second.memory;
ATRACE_INT64(it.first.c_str(), it.second.memory);
if (it.second.purgeableMemory != INVALID_MEMORY_SIZE) {
ATRACE_INT64((std::string("Purgeable ") + it.first).c_str(), it.second.purgeableMemory);
}
}
ATRACE_INT64("HWUI All Memory", hwui_all_frame_memory);
}
/**
* recordAndResetCountersIfNeeded reads memory usage from mLastDumpValue/mLastPurgeableDumpValue and
* accumulates in mCurrentValues[category]. It makes provision to create a new category and track
* purgeable memory only if there is at least one observation.
* recordAndResetCountersIfNeeded won't do anything until all the information for a given dumpName
* is received.
*/
void ATraceMemoryDump::recordAndResetCountersIfNeeded(const char* dumpName) {
if (!mLastDumpName.compare(dumpName)) {
// Still waiting for more data for current dumpName.
return;
}
// First invocation will have an empty mLastDumpName.
if (!mLastDumpName.empty()) {
// A new dumpName observed -> store the data already collected.
auto memoryCounter = mCurrentValues.find(mCategory);
if (memoryCounter != mCurrentValues.end()) {
memoryCounter->second.memory += mLastDumpValue;
if (mLastPurgeableDumpValue != INVALID_MEMORY_SIZE) {
if (memoryCounter->second.purgeableMemory == INVALID_MEMORY_SIZE) {
memoryCounter->second.purgeableMemory = mLastPurgeableDumpValue;
} else {
memoryCounter->second.purgeableMemory += mLastPurgeableDumpValue;
}
}
} else {
mCurrentValues[mCategory] = {mLastDumpValue, mLastPurgeableDumpValue};
}
}
// Reset counters and default category for the newly observed "dumpName".
resetCurrentCounter(dumpName);
}
void ATraceMemoryDump::resetCurrentCounter(const char* dumpName) {
mLastDumpValue = 0;
mLastPurgeableDumpValue = INVALID_MEMORY_SIZE;
mLastDumpName = dumpName;
// Categories not listed in sResourceMap are reported as "Misc Memory"
mCategory = "HWUI Misc Memory";
}
} /* namespace skiapipeline */
} /* namespace uirenderer */
} /* namespace android */
|