summaryrefslogtreecommitdiff
path: root/libs/renderengine/skia/debug/SkiaMemoryReporter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/renderengine/skia/debug/SkiaMemoryReporter.cpp')
-rw-r--r--libs/renderengine/skia/debug/SkiaMemoryReporter.cpp205
1 files changed, 205 insertions, 0 deletions
diff --git a/libs/renderengine/skia/debug/SkiaMemoryReporter.cpp b/libs/renderengine/skia/debug/SkiaMemoryReporter.cpp
new file mode 100644
index 0000000000..f24a4f1c05
--- /dev/null
+++ b/libs/renderengine/skia/debug/SkiaMemoryReporter.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+
+#include "SkiaMemoryReporter.h"
+
+#include <SkString.h>
+#include <android-base/stringprintf.h>
+#include <log/log_main.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+using base::StringAppendF;
+
+SkiaMemoryReporter::SkiaMemoryReporter(const std::vector<ResourcePair>& resourceMap, bool itemize)
+ : mResourceMap(resourceMap),
+ mItemize(itemize),
+ mTotalSize("bytes", 0),
+ mPurgeableSize("bytes", 0) {}
+
+const char* SkiaMemoryReporter::mapName(const char* resourceName) {
+ for (auto& resource : mResourceMap) {
+ if (SkStrContains(resourceName, resource.first)) {
+ return resource.second;
+ }
+ }
+ return nullptr;
+}
+
+void SkiaMemoryReporter::resetCurrentElement() {
+ mCurrentElement.clear();
+ mCurrentValues.clear();
+ mIsCurrentValueWrapped = false;
+}
+
+void SkiaMemoryReporter::processCurrentElement() {
+ // compute the top level element name using the map
+ const char* resourceName = mCurrentElement.empty() ? nullptr : mapName(mCurrentElement.c_str());
+
+ // if we don't have a resource name then we don't know how to label the
+ // data and should abort.
+ if (resourceName == nullptr) {
+ resetCurrentElement();
+ return;
+ }
+
+ // Only count elements that contain "size"; other values just provide metadata.
+ auto sizeResult = mCurrentValues.find("size");
+ if (sizeResult != mCurrentValues.end() && sizeResult->second.value > 0) {
+ if (!mIsCurrentValueWrapped) {
+ mTotalSize.value += sizeResult->second.value;
+ mTotalSize.count++;
+ }
+ } else {
+ resetCurrentElement();
+ return;
+ }
+
+ // find the purgeable size if one exists
+ auto purgeableResult = mCurrentValues.find("purgeable_size");
+ if (!mIsCurrentValueWrapped && purgeableResult != mCurrentValues.end()) {
+ mPurgeableSize.value += purgeableResult->second.value;
+ mPurgeableSize.count++;
+ }
+
+ // do we store this element in the wrapped list or the skia managed list
+ auto& results = mIsCurrentValueWrapped ? mWrappedResults : mResults;
+
+ // insert a copy of the element and all of its keys. We must make a copy here instead of
+ // std::move() as we will continue to use these values later in the function and again
+ // when we move on to process the next element.
+ results.insert({mCurrentElement, mCurrentValues});
+
+ // insert the item into its mapped category
+ auto result = results.find(resourceName);
+ if (result != results.end()) {
+ auto& resourceValues = result->second;
+ auto totalResult = resourceValues.find(sizeResult->first);
+ if (totalResult != resourceValues.end()) {
+ ALOGE_IF(sizeResult->second.units != totalResult->second.units,
+ "resource units do not match so the sum of resource type (%s) will be invalid",
+ resourceName);
+ totalResult->second.value += sizeResult->second.value;
+ totalResult->second.count++;
+ } else {
+ ALOGE("an entry (%s) should not exist in the results without a size", resourceName);
+ }
+ } else {
+ // only store the size for the top level resource
+ results.insert({resourceName, {{sizeResult->first, sizeResult->second}}});
+ }
+
+ resetCurrentElement();
+}
+
+void SkiaMemoryReporter::dumpNumericValue(const char* dumpName, const char* valueName,
+ const char* units, uint64_t value) {
+ if (mCurrentElement != dumpName) {
+ processCurrentElement();
+ mCurrentElement = dumpName;
+ }
+ mCurrentValues.insert({valueName, {units, value}});
+}
+
+void SkiaMemoryReporter::dumpWrappedState(const char* dumpName, bool isWrappedObject) {
+ if (mCurrentElement != dumpName) {
+ processCurrentElement();
+ mCurrentElement = dumpName;
+ }
+ mIsCurrentValueWrapped = isWrappedObject;
+}
+
+void SkiaMemoryReporter::logOutput(std::string& log, bool wrappedResources) {
+ // process the current element before logging
+ processCurrentElement();
+
+ const auto& resultsMap = wrappedResources ? mWrappedResults : mResults;
+
+ // log each individual element based on the resource map
+ for (const auto& resourceCategory : mResourceMap) {
+ // find the named item and print the totals
+ const auto categoryItem = resultsMap.find(resourceCategory.second);
+ if (categoryItem != resultsMap.end()) {
+ auto result = categoryItem->second.find("size");
+ if (result != categoryItem->second.end()) {
+ TraceValue traceValue = convertUnits(result->second);
+ const char* entry = (traceValue.count > 1) ? "entries" : "entry";
+ StringAppendF(&log, " %s: %.2f %s (%d %s)\n", categoryItem->first.c_str(),
+ traceValue.value, traceValue.units, traceValue.count, entry);
+ }
+ if (mItemize) {
+ for (const auto& individualItem : resultsMap) {
+ // if the individual item matches the category then print all its details or
+ // in the case of wrapped resources just print the wrapped size
+ const char* categoryMatch = mapName(individualItem.first.c_str());
+ if (categoryMatch && strcmp(categoryMatch, resourceCategory.second) == 0) {
+ auto result = individualItem.second.find("size");
+ TraceValue size = convertUnits(result->second);
+ StringAppendF(&log, " %s: size[%.2f %s]", individualItem.first.c_str(),
+ size.value, size.units);
+ if (!wrappedResources) {
+ for (const auto& itemValues : individualItem.second) {
+ if (strcmp("size", itemValues.first) == 0) {
+ continue;
+ }
+ TraceValue traceValue = convertUnits(itemValues.second);
+ if (traceValue.value == 0.0f) {
+ StringAppendF(&log, " %s[%s]", itemValues.first,
+ traceValue.units);
+ } else {
+ StringAppendF(&log, " %s[%.2f %s]", itemValues.first,
+ traceValue.value, traceValue.units);
+ }
+ }
+ }
+ StringAppendF(&log, "\n");
+ }
+ }
+ }
+ }
+ }
+}
+
+void SkiaMemoryReporter::logTotals(std::string& log) {
+ // process the current element before logging
+ processCurrentElement();
+
+ TraceValue total = convertUnits(mTotalSize);
+ TraceValue purgeable = convertUnits(mPurgeableSize);
+ StringAppendF(&log, " %.0f bytes, %.2f %s (%.2f %s is purgeable)\n", mTotalSize.value,
+ total.value, total.units, purgeable.value, purgeable.units);
+}
+
+SkiaMemoryReporter::TraceValue SkiaMemoryReporter::convertUnits(const TraceValue& value) {
+ TraceValue output(value);
+ if (SkString("bytes") == SkString(output.units) && output.value >= 1024) {
+ output.value = output.value / 1024.0f;
+ output.units = "KB";
+ }
+ if (SkString("KB") == SkString(output.units) && output.value >= 1024) {
+ output.value = output.value / 1024.0f;
+ output.units = "MB";
+ }
+ return output;
+}
+
+} /* namespace skia */
+} /* namespace renderengine */
+} /* namespace android */