1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "SkiaMemoryTracer.h"
18
19 namespace android {
20 namespace uirenderer {
21 namespace skiapipeline {
22
SkiaMemoryTracer(std::vector<ResourcePair> resourceMap,bool itemizeType)23 SkiaMemoryTracer::SkiaMemoryTracer(std::vector<ResourcePair> resourceMap, bool itemizeType)
24 : mResourceMap(resourceMap)
25 , mItemizeType(itemizeType)
26 , mTotalSize("bytes", 0)
27 , mPurgeableSize("bytes", 0) {}
28
SkiaMemoryTracer(const char * categoryKey,bool itemizeType)29 SkiaMemoryTracer::SkiaMemoryTracer(const char* categoryKey, bool itemizeType)
30 : mCategoryKey(categoryKey)
31 , mItemizeType(itemizeType)
32 , mTotalSize("bytes", 0)
33 , mPurgeableSize("bytes", 0) {}
34
mapName(const std::string & resourceName)35 std::optional<std::string> SkiaMemoryTracer::mapName(const std::string& resourceName) {
36 for (auto& resource : mResourceMap) {
37 if (resourceName.find(resource.first) != std::string::npos) {
38 return resource.second;
39 }
40 }
41 return std::nullopt;
42 }
43
processElement()44 void SkiaMemoryTracer::processElement() {
45 if (!mCurrentElement.empty()) {
46 // Only count elements that contain "size", other values just provide metadata.
47 auto sizeResult = mCurrentValues.find("size");
48 if (sizeResult != mCurrentValues.end()) {
49 mTotalSize.value += sizeResult->second.value;
50 mTotalSize.count++;
51 } else {
52 mCurrentElement.clear();
53 mCurrentValues.clear();
54 return;
55 }
56
57 // find the purgeable size if one exists
58 auto purgeableResult = mCurrentValues.find("purgeable_size");
59 if (purgeableResult != mCurrentValues.end()) {
60 mPurgeableSize.value += purgeableResult->second.value;
61 mPurgeableSize.count++;
62 }
63
64 // find the type if one exists
65 std::string type;
66 auto typeResult = mCurrentValues.find("type");
67 if (typeResult != mCurrentValues.end()) {
68 type = typeResult->second.units;
69 } else if (mItemizeType) {
70 type = "Other";
71 }
72
73 // compute the type if we are itemizing or use the default "size" if we are not
74 std::string key = (mItemizeType) ? type : sizeResult->first;
75
76 // compute the top level element name using either the map or category key
77 std::optional<std::string> resourceName = mapName(mCurrentElement);
78 if (mCategoryKey) {
79 // find the category if one exists
80 auto categoryResult = mCurrentValues.find(*mCategoryKey);
81 if (categoryResult != mCurrentValues.end()) {
82 resourceName = categoryResult->second.units;
83 } else if (mItemizeType) {
84 resourceName = "Other";
85 }
86 }
87
88 // if we don't have a pretty name then use the dumpName
89 if (!resourceName) {
90 resourceName = mCurrentElement;
91 }
92
93 auto result = mResults.find(*resourceName);
94 if (result != mResults.end()) {
95 auto& resourceValues = result->second;
96 typeResult = resourceValues.find(key);
97 if (typeResult != resourceValues.end()) {
98 SkASSERT(sizeResult->second.units == typeResult->second.units);
99 typeResult->second.value += sizeResult->second.value;
100 typeResult->second.count++;
101 } else {
102 resourceValues.insert({key, sizeResult->second});
103 }
104 } else {
105 TraceValue sizeValue = sizeResult->second;
106 mCurrentValues.clear();
107 mCurrentValues.insert({key, sizeValue});
108 mResults.insert({*resourceName, mCurrentValues});
109 }
110 }
111
112 mCurrentElement.clear();
113 mCurrentValues.clear();
114 }
115
dumpNumericValue(const char * dumpName,const char * valueName,const char * units,uint64_t value)116 void SkiaMemoryTracer::dumpNumericValue(const char* dumpName, const char* valueName,
117 const char* units, uint64_t value) {
118 if (mCurrentElement != dumpName) {
119 processElement();
120 mCurrentElement = dumpName;
121 }
122 mCurrentValues.insert({valueName, {units, value}});
123 }
124
hasOutput()125 bool SkiaMemoryTracer::hasOutput() {
126 // process any remaining elements
127 processElement();
128 return mResults.size() > 0;
129 }
130
logOutput(String8 & log)131 void SkiaMemoryTracer::logOutput(String8& log) {
132 // process any remaining elements
133 processElement();
134
135 for (const auto& namedItem : mResults) {
136 if (mItemizeType) {
137 log.appendFormat(" %s:\n", namedItem.first.c_str());
138 for (const auto& typedValue : namedItem.second) {
139 TraceValue traceValue = convertUnits(typedValue.second);
140 const char* entry = (traceValue.count > 1) ? "entries" : "entry";
141 log.appendFormat(" %s: %.2f %s (%d %s)\n", typedValue.first.c_str(),
142 traceValue.value, traceValue.units.c_str(), traceValue.count,
143 entry);
144 }
145 } else {
146 auto result = namedItem.second.find("size");
147 if (result != namedItem.second.end()) {
148 TraceValue traceValue = convertUnits(result->second);
149 const char* entry = (traceValue.count > 1) ? "entries" : "entry";
150 log.appendFormat(" %s: %.2f %s (%d %s)\n", namedItem.first.c_str(),
151 traceValue.value, traceValue.units.c_str(), traceValue.count,
152 entry);
153 }
154 }
155 }
156 }
157
total()158 size_t SkiaMemoryTracer::total() {
159 processElement();
160 if ("bytes" == mTotalSize.units) {
161 return mTotalSize.value;
162 }
163 return 0;
164 }
165
logTotals(String8 & log)166 void SkiaMemoryTracer::logTotals(String8& log) {
167 TraceValue total = convertUnits(mTotalSize);
168 TraceValue purgeable = convertUnits(mPurgeableSize);
169 log.appendFormat(" %.0f bytes, %.2f %s (%.2f %s is purgeable)\n", mTotalSize.value,
170 total.value, total.units.c_str(), purgeable.value, purgeable.units.c_str());
171 }
172
convertUnits(const TraceValue & value)173 SkiaMemoryTracer::TraceValue SkiaMemoryTracer::convertUnits(const TraceValue& value) {
174 TraceValue output(value);
175 if ("bytes" == output.units && output.value >= 1024) {
176 output.value = output.value / 1024.0f;
177 output.units = "KB";
178 }
179 if ("KB" == output.units && output.value >= 1024) {
180 output.value = output.value / 1024.0f;
181 output.units = "MB";
182 }
183 return output;
184 }
185
186 } /* namespace skiapipeline */
187 } /* namespace uirenderer */
188 } /* namespace android */
189