• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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         const char* type;
62         auto typeResult = currentValues_.find("type");
63         if (typeResult != currentValues_.end()) {
64             type = typeResult->second.units;
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;
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             TraceValue sizeValue = sizeResult->second;
93             currentValues_.clear();
94             currentValues_.insert({ key, sizeValue });
95             results_.insert({ resourceName, currentValues_ });
96         } else {
97             auto& resourceValues = result->second;
98             typeResult = resourceValues.find(key);
99             if (typeResult == resourceValues.end()) {
100                 resourceValues.insert({ key, sizeResult->second });
101             } else {
102                 typeResult->second.value += sizeResult->second.value;
103                 typeResult->second.count++;
104             }
105         }
106     }
107 
108     currentElement_.clear();
109     currentValues_.clear();
110 }
111 
dumpNumericValue(const char * dumpName,const char * valueName,const char * units,uint64_t value)112 void SkiaMemoryTracer::dumpNumericValue(const char* dumpName, const char* valueName, const char* units, uint64_t value)
113 {
114     if (currentElement_ != dumpName) {
115         ProcessElement();
116         currentElement_ = dumpName;
117     }
118     currentValues_.insert({ valueName, { units, value } });
119 }
120 
LogOutput(DfxString & log)121 void SkiaMemoryTracer::LogOutput(DfxString& log)
122 {
123     // process any remaining elements
124     ProcessElement();
125 
126     for (const auto& namedItem : results_) {
127         if (itemizeType_) {
128             log.AppendFormat("  %s:\n", namedItem.first.c_str()); // skia/sk_glyph_cache
129             for (const auto& typedValue : namedItem.second) {
130                 TraceValue traceValue = ConvertUnits(typedValue.second);
131                 const char* entry = (traceValue.count > 1) ? "entries" : "entry";
132                 log.AppendFormat("    %s: %.2f %s (%d %s)\n", typedValue.first.c_str(), traceValue.value,
133                     traceValue.units, traceValue.count, entry);
134             }
135         } else {
136             auto result = namedItem.second.find("size");
137             if (result != namedItem.second.end()) {
138                 TraceValue traceValue = ConvertUnits(result->second);
139                 const char* entry = (traceValue.count > 1) ? "entries" : "entry";
140                 log.AppendFormat("  %s: %.2f %s (%d %s)\n", namedItem.first.c_str(), traceValue.value, traceValue.units,
141                     traceValue.count, entry);
142             }
143         }
144     }
145 }
146 
GetGLMemorySize()147 float SkiaMemoryTracer::GetGLMemorySize()
148 {
149     ProcessElement();
150     // exclude scratch memory
151     // cause scratch memory is generated by animation and effect. which is not response by app
152     size_t totalGpuSizeApp = 0;
153     for (const auto& namedItem : results_) {
154         if (namedItem.first == "Scratch" || namedItem.first == "skia/gr_text_blob_cache") {
155             continue;
156         }
157         totalGpuSizeApp = std::accumulate(namedItem.second.begin(), namedItem.second.end(), totalGpuSizeApp,
158             [](int total, const auto& typedValue) { return total + typedValue.second.value; });
159     }
160     return totalGpuSizeApp;
161 }
162 
GetGpuMemorySizeInMB()163 float SkiaMemoryTracer::GetGpuMemorySizeInMB()
164 {
165     ProcessElement();
166     return ConvertToMB(totalSize_);
167 }
168 
LogTotals(DfxString & log)169 void SkiaMemoryTracer::LogTotals(DfxString& log)
170 {
171     TraceValue total = ConvertUnits(totalSize_);
172     TraceValue purgeable = ConvertUnits(purgeableSize_);
173     log.AppendFormat("    %.0f bytes, %.2f %s (%.2f %s is purgeable)\n", totalSize_.value, total.value, total.units,
174         purgeable.value, purgeable.units);
175 }
176 
ConvertUnits(const TraceValue & value)177 SkiaMemoryTracer::TraceValue SkiaMemoryTracer::ConvertUnits(const TraceValue& value)
178 {
179     TraceValue output(value);
180     if (SkString(output.units) == SkString("bytes") && output.value >= MEMUNIT_RATE) {
181         output.value = output.value / MEMUNIT_RATE;
182         output.units = "KB";
183     }
184     if (SkString(output.units) == SkString("KB") && output.value >= MEMUNIT_RATE) {
185         output.value = output.value / MEMUNIT_RATE;
186         output.units = "MB";
187     }
188     return output;
189 }
190 
ConvertToMB(const TraceValue & value)191 float SkiaMemoryTracer::ConvertToMB(const TraceValue& value)
192 {
193     if (SkString(value.units) == SkString("bytes")) {
194         return value.value / MEMUNIT_RATE / MEMUNIT_RATE;
195     }
196     if (SkString(value.units) == SkString("KB")) {
197         return value.value / MEMUNIT_RATE;
198     }
199     return value.value;
200 }
201 
202 } // namespace OHOS::Rosen