1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "rs_skia_memory_tracer.h"
17 #include <numeric>
18 namespace OHOS::Rosen {
19 constexpr uint32_t MEMUNIT_RATE = 1024;
20
SkiaMemoryTracer(const std::vector<ResourcePair> & resourceMap,bool itemizeType)21 SkiaMemoryTracer::SkiaMemoryTracer(const std::vector<ResourcePair>& resourceMap, bool itemizeType)
22 : resourceMap_(resourceMap), itemizeType_(itemizeType), totalSize_("bytes", 0), purgeableSize_("bytes", 0)
23 {}
24
SkiaMemoryTracer(const char * categoryKey,bool itemizeType)25 SkiaMemoryTracer::SkiaMemoryTracer(const char* categoryKey, bool itemizeType)
26 : categoryKey_(categoryKey), itemizeType_(itemizeType), totalSize_("bytes", 0), purgeableSize_("bytes", 0)
27 {}
28
MapName(const char * resourceName)29 const char* SkiaMemoryTracer::MapName(const char* resourceName)
30 {
31 for (auto& resource : resourceMap_) {
32 if (SkStrContains(resourceName, resource.first)) {
33 return resource.second;
34 }
35 }
36 return nullptr;
37 }
38
ProcessElement()39 void SkiaMemoryTracer::ProcessElement()
40 {
41 if (!currentElement_.empty()) {
42 // Only count elements that contain "size", other values just provide metadata.
43 auto sizeResult = currentValues_.find("size");
44 if (sizeResult != currentValues_.end()) {
45 totalSize_.value += sizeResult->second.value;
46 totalSize_.count++;
47 } else {
48 currentElement_.clear();
49 currentValues_.clear();
50 return;
51 }
52
53 // find the purgeable size if one exists
54 auto purgeableResult = currentValues_.find("purgeable_size");
55 if (purgeableResult != currentValues_.end()) {
56 purgeableSize_.value += purgeableResult->second.value;
57 purgeableSize_.count++;
58 }
59
60 // find the type if one exists
61 std::string type;
62 auto typeResult = currentValues_.find("type");
63 if (typeResult != currentValues_.end()) {
64 type = typeResult->second.units.c_str();
65 } else if (itemizeType_) {
66 type = "Other";
67 } else {
68 type = "";
69 }
70
71 // compute the type if we are itemizing or use the default "size" if we are not
72 std::string key = (itemizeType_) ? type : sizeResult->first;
73
74 // compute the top level element name using either the map or category key
75 const char* resourceName = MapName(currentElement_.c_str());
76 if (categoryKey_ != nullptr) {
77 // find the category if one exists
78 auto categoryResult = currentValues_.find(categoryKey_);
79 if (categoryResult != currentValues_.end()) {
80 resourceName = categoryResult->second.units.c_str();
81 }
82 }
83
84 // if we don't have a resource name then we don't know how to label the
85 // data and should abort.
86 if (resourceName == nullptr) {
87 resourceName = currentElement_.c_str();
88 }
89
90 auto result = results_.find(resourceName);
91 if (result == results_.end()) {
92 std::string strResourceName = resourceName;
93 TraceValue sizeValue = sizeResult->second;
94 currentValues_.clear();
95 currentValues_.insert({ key, sizeValue });
96 results_.insert({ strResourceName, currentValues_ });
97 } else {
98 auto& resourceValues = result->second;
99 typeResult = resourceValues.find(key);
100 if (typeResult == resourceValues.end()) {
101 resourceValues.insert({ key, sizeResult->second });
102 } else {
103 typeResult->second.value += sizeResult->second.value;
104 typeResult->second.count++;
105 }
106 }
107 }
108
109 currentElement_.clear();
110 currentValues_.clear();
111 }
112
dumpNumericValue(const char * dumpName,const char * valueName,const char * units,uint64_t value)113 void SkiaMemoryTracer::dumpNumericValue(const char* dumpName, const char* valueName, const char* units, uint64_t value)
114 {
115 if (currentElement_ != dumpName) {
116 ProcessElement();
117 currentElement_ = dumpName;
118 }
119 currentValues_.insert({ valueName, { units, value } });
120 }
121
LogOutput(DfxString & log)122 void SkiaMemoryTracer::LogOutput(DfxString& log)
123 {
124 // process any remaining elements
125 ProcessElement();
126
127 for (const auto& namedItem : results_) {
128 if (itemizeType_) {
129 log.AppendFormat(" %s:\n", namedItem.first.c_str()); // skia/sk_glyph_cache
130 for (const auto& typedValue : namedItem.second) {
131 TraceValue traceValue = ConvertUnits(typedValue.second);
132 const char* entry = (traceValue.count > 1) ? "entries" : "entry";
133 log.AppendFormat(" %s: %.2f %s (%d %s)\n", typedValue.first.c_str(), traceValue.value,
134 traceValue.units.c_str(), traceValue.count, entry);
135 }
136 } else {
137 auto result = namedItem.second.find("size");
138 if (result != namedItem.second.end()) {
139 TraceValue traceValue = ConvertUnits(result->second);
140 const char* entry = (traceValue.count > 1) ? "entries" : "entry";
141 log.AppendFormat(" %s: %.2f %s (%d %s)\n", namedItem.first.c_str(), traceValue.value,
142 traceValue.units.c_str(), traceValue.count, entry);
143 }
144 }
145 }
146 }
147
GetGLMemorySize()148 float SkiaMemoryTracer::GetGLMemorySize()
149 {
150 ProcessElement();
151 // exclude scratch memory
152 // cause scratch memory is generated by animation and effect. which is not response by app
153 size_t totalGpuSizeApp = 0;
154 for (const auto& namedItem : results_) {
155 if (namedItem.first == "Scratch" || namedItem.first == "skia/gr_text_blob_cache") {
156 continue;
157 }
158 totalGpuSizeApp = std::accumulate(namedItem.second.begin(), namedItem.second.end(), totalGpuSizeApp,
159 [](int total, const auto& typedValue) { return total + typedValue.second.value; });
160 }
161 return totalGpuSizeApp;
162 }
163
GetGpuMemorySizeInMB()164 float SkiaMemoryTracer::GetGpuMemorySizeInMB()
165 {
166 ProcessElement();
167 return ConvertToMB(totalSize_);
168 }
169
LogTotals(DfxString & log)170 void SkiaMemoryTracer::LogTotals(DfxString& log)
171 {
172 TraceValue total = ConvertUnits(totalSize_);
173 TraceValue purgeable = ConvertUnits(purgeableSize_);
174 log.AppendFormat(" %.0f bytes, %.2f %s (%.2f %s is purgeable)\n", totalSize_.value, total.value,
175 total.units.c_str(), purgeable.value, purgeable.units.c_str());
176 }
177
ConvertUnits(const TraceValue & value)178 SkiaMemoryTracer::TraceValue SkiaMemoryTracer::ConvertUnits(const TraceValue& value)
179 {
180 TraceValue output(value);
181 if (output.units == SkString("bytes") && output.value >= MEMUNIT_RATE) {
182 output.value = output.value / MEMUNIT_RATE;
183 output.units = "KB";
184 }
185 if (output.units == SkString("KB") && output.value >= MEMUNIT_RATE) {
186 output.value = output.value / MEMUNIT_RATE;
187 output.units = "MB";
188 }
189 return output;
190 }
191
ConvertToMB(const TraceValue & value)192 float SkiaMemoryTracer::ConvertToMB(const TraceValue& value)
193 {
194 if (value.units == SkString("bytes")) {
195 return value.value / MEMUNIT_RATE / MEMUNIT_RATE;
196 }
197 if (value.units == SkString("KB")) {
198 return value.value / MEMUNIT_RATE;
199 }
200 return value.value;
201 }
202
203 } // namespace OHOS::Rosen