|
| 1 | +// Copyright 2026 The Cobalt Authors. All Rights Reserved. |
| 2 | +// |
| 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 | +#include "cobalt/browser/metrics/cobalt_memory_metrics_emitter.h" |
| 16 | + |
| 17 | +#include <string> |
| 18 | +#include <string_view> |
| 19 | +#include <utility> |
| 20 | + |
| 21 | +#include "base/functional/bind.h" |
| 22 | +#include "base/logging.h" |
| 23 | +#include "base/metrics/histogram_functions.h" |
| 24 | +#include "base/metrics/histogram_macros.h" |
| 25 | +#include "base/strings/strcat.h" |
| 26 | +#include "base/task/sequenced_task_runner.h" |
| 27 | +#include "base/trace_event/memory_dump_request_args.h" |
| 28 | +#include "build/build_config.h" |
| 29 | +#include "services/resource_coordinator/public/cpp/memory_instrumentation/browser_metrics.h" |
| 30 | +#include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h" |
| 31 | + |
| 32 | +#if BUILDFLAG(IS_ANDROID) |
| 33 | +#include "base/android/meminfo_dump_provider.h" |
| 34 | +#endif |
| 35 | + |
| 36 | +using base::trace_event::MemoryAllocatorDump; |
| 37 | +using memory_instrumentation::GetPrivateFootprintHistogramName; |
| 38 | +using memory_instrumentation::GlobalMemoryDump; |
| 39 | +using memory_instrumentation::HistogramProcessType; |
| 40 | +using memory_instrumentation::HistogramProcessTypeToString; |
| 41 | +using memory_instrumentation::kMemoryHistogramPrefix; |
| 42 | + |
| 43 | +namespace cobalt { |
| 44 | + |
| 45 | +namespace { |
| 46 | + |
| 47 | +const char kEffectiveSize[] = "effective_size"; |
| 48 | +const char kAllocatedObjectsSize[] = "allocated_objects_size"; |
| 49 | + |
| 50 | +constexpr int kKiB = 1024; |
| 51 | +constexpr int kMiB = 1024 * 1024; |
| 52 | + |
| 53 | +const CobaltMemoryMetricsEmitter::Metric kAllocatorDumpNamesForMetrics[] = { |
| 54 | + {"blink_gc", |
| 55 | + "BlinkGC", |
| 56 | + CobaltMemoryMetricsEmitter::MetricSize::kLarge, |
| 57 | + kEffectiveSize, |
| 58 | + CobaltMemoryMetricsEmitter::EmitTo::kSizeInUkmAndUma, |
| 59 | + {}}, |
| 60 | + {"blink_gc", |
| 61 | + "BlinkGC.AllocatedObjects", |
| 62 | + CobaltMemoryMetricsEmitter::MetricSize::kLarge, |
| 63 | + kAllocatedObjectsSize, |
| 64 | + CobaltMemoryMetricsEmitter::EmitTo::kSizeInUkmAndUma, |
| 65 | + {}}, |
| 66 | + {"blink_objects/Document", |
| 67 | + "NumberOfDocuments", |
| 68 | + CobaltMemoryMetricsEmitter::MetricSize::kTiny, |
| 69 | + MemoryAllocatorDump::kNameObjectCount, |
| 70 | + CobaltMemoryMetricsEmitter::EmitTo::kCountsInUkmAndSizeInUma, |
| 71 | + {}}, |
| 72 | + {"blink_objects/Frame", |
| 73 | + "NumberOfFrames", |
| 74 | + CobaltMemoryMetricsEmitter::MetricSize::kTiny, |
| 75 | + MemoryAllocatorDump::kNameObjectCount, |
| 76 | + CobaltMemoryMetricsEmitter::EmitTo::kCountsInUkmAndSizeInUma, |
| 77 | + {}}, |
| 78 | + {"blink_objects/LayoutObject", |
| 79 | + "NumberOfLayoutObjects", |
| 80 | + CobaltMemoryMetricsEmitter::MetricSize::kTiny, |
| 81 | + MemoryAllocatorDump::kNameObjectCount, |
| 82 | + CobaltMemoryMetricsEmitter::EmitTo::kCountsInUkmAndSizeInUma, |
| 83 | + {}}, |
| 84 | + {"blink_objects/Node", |
| 85 | + "NumberOfNodes", |
| 86 | + CobaltMemoryMetricsEmitter::MetricSize::kSmall, |
| 87 | + MemoryAllocatorDump::kNameObjectCount, |
| 88 | + CobaltMemoryMetricsEmitter::EmitTo::kCountsInUkmAndSizeInUma, |
| 89 | + {}}, |
| 90 | + {"font_caches", |
| 91 | + "FontCaches", |
| 92 | + CobaltMemoryMetricsEmitter::MetricSize::kSmall, |
| 93 | + kEffectiveSize, |
| 94 | + CobaltMemoryMetricsEmitter::EmitTo::kSizeInUkmAndUma, |
| 95 | + {}}, |
| 96 | + {"java_heap", |
| 97 | + "JavaHeap", |
| 98 | + CobaltMemoryMetricsEmitter::MetricSize::kLarge, |
| 99 | + kEffectiveSize, |
| 100 | + CobaltMemoryMetricsEmitter::EmitTo::kSizeInUkmAndUma, |
| 101 | + {}}, |
| 102 | + {"leveldatabase", |
| 103 | + "LevelDatabase", |
| 104 | + CobaltMemoryMetricsEmitter::MetricSize::kSmall, |
| 105 | + kEffectiveSize, |
| 106 | + CobaltMemoryMetricsEmitter::EmitTo::kSizeInUkmAndUma, |
| 107 | + {}}, |
| 108 | + {"malloc", |
| 109 | + "Malloc", |
| 110 | + CobaltMemoryMetricsEmitter::MetricSize::kLarge, |
| 111 | + kEffectiveSize, |
| 112 | + CobaltMemoryMetricsEmitter::EmitTo::kSizeInUkmAndUma, |
| 113 | + {}}, |
| 114 | + {"malloc", |
| 115 | + "Malloc.AllocatedObjects", |
| 116 | + CobaltMemoryMetricsEmitter::MetricSize::kLarge, |
| 117 | + kAllocatedObjectsSize, |
| 118 | + CobaltMemoryMetricsEmitter::EmitTo::kSizeInUkmAndUma, |
| 119 | + {}}, |
| 120 | + {"partition_alloc", |
| 121 | + "PartitionAlloc", |
| 122 | + CobaltMemoryMetricsEmitter::MetricSize::kLarge, |
| 123 | + kEffectiveSize, |
| 124 | + CobaltMemoryMetricsEmitter::EmitTo::kSizeInUkmAndUma, |
| 125 | + {}}, |
| 126 | + {"partition_alloc/allocated_objects", |
| 127 | + "PartitionAlloc.AllocatedObjects", |
| 128 | + CobaltMemoryMetricsEmitter::MetricSize::kLarge, |
| 129 | + kEffectiveSize, |
| 130 | + CobaltMemoryMetricsEmitter::EmitTo::kSizeInUkmAndUma, |
| 131 | + {}}, |
| 132 | + {"skia", |
| 133 | + "Skia", |
| 134 | + CobaltMemoryMetricsEmitter::MetricSize::kLarge, |
| 135 | + kEffectiveSize, |
| 136 | + CobaltMemoryMetricsEmitter::EmitTo::kSizeInUkmAndUma, |
| 137 | + {}}, |
| 138 | + {"sqlite", |
| 139 | + "Sqlite", |
| 140 | + CobaltMemoryMetricsEmitter::MetricSize::kSmall, |
| 141 | + kEffectiveSize, |
| 142 | + CobaltMemoryMetricsEmitter::EmitTo::kSizeInUkmAndUma, |
| 143 | + {}}, |
| 144 | + {"ui", |
| 145 | + "UI", |
| 146 | + CobaltMemoryMetricsEmitter::MetricSize::kSmall, |
| 147 | + kEffectiveSize, |
| 148 | + CobaltMemoryMetricsEmitter::EmitTo::kSizeInUkmAndUma, |
| 149 | + {}}, |
| 150 | + {"v8", |
| 151 | + "V8", |
| 152 | + CobaltMemoryMetricsEmitter::MetricSize::kLarge, |
| 153 | + kEffectiveSize, |
| 154 | + CobaltMemoryMetricsEmitter::EmitTo::kSizeInUkmAndUma, |
| 155 | + {}}, |
| 156 | + {"v8", |
| 157 | + "V8.AllocatedObjects", |
| 158 | + CobaltMemoryMetricsEmitter::MetricSize::kLarge, |
| 159 | + kAllocatedObjectsSize, |
| 160 | + CobaltMemoryMetricsEmitter::EmitTo::kSizeInUkmAndUma, |
| 161 | + {}}, |
| 162 | +#if BUILDFLAG(IS_ANDROID) |
| 163 | + {base::android::MeminfoDumpProvider::kDumpName, |
| 164 | + "AndroidOtherPss", |
| 165 | + CobaltMemoryMetricsEmitter::MetricSize::kLarge, |
| 166 | + base::android::MeminfoDumpProvider::kPssMetricName, |
| 167 | + CobaltMemoryMetricsEmitter::EmitTo::kSizeInUmaOnly, |
| 168 | + {}}, |
| 169 | +#endif |
| 170 | +}; |
| 171 | + |
| 172 | +constexpr char kExperimentalUmaPrefix[] = "Memory.Experimental."; |
| 173 | +constexpr char kVersionSuffixNormal[] = "2."; |
| 174 | +constexpr char kVersionSuffixSmall[] = "2.Small."; |
| 175 | +constexpr char kVersionSuffixTiny[] = "2.Tiny."; |
| 176 | + |
| 177 | +static const char* MetricSizeToVersionSuffix( |
| 178 | + CobaltMemoryMetricsEmitter::MetricSize size) { |
| 179 | + switch (size) { |
| 180 | + case CobaltMemoryMetricsEmitter::MetricSize::kPercentage: |
| 181 | + return kVersionSuffixNormal; |
| 182 | + case CobaltMemoryMetricsEmitter::MetricSize::kLarge: |
| 183 | + return kVersionSuffixNormal; |
| 184 | + case CobaltMemoryMetricsEmitter::MetricSize::kSmall: |
| 185 | + return kVersionSuffixSmall; |
| 186 | + case CobaltMemoryMetricsEmitter::MetricSize::kTiny: |
| 187 | + return kVersionSuffixTiny; |
| 188 | + case CobaltMemoryMetricsEmitter::MetricSize::kCustom: |
| 189 | + return kVersionSuffixNormal; |
| 190 | + } |
| 191 | +} |
| 192 | + |
| 193 | +} // namespace |
| 194 | + |
| 195 | +CobaltMemoryMetricsEmitter::CobaltMemoryMetricsEmitter() = default; |
| 196 | + |
| 197 | +void CobaltMemoryMetricsEmitter::FetchAndEmitProcessMemoryMetrics() { |
| 198 | + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 199 | + |
| 200 | + memory_dump_in_progress_ = true; |
| 201 | + |
| 202 | + auto* instrumentation = |
| 203 | + memory_instrumentation::MemoryInstrumentation::GetInstance(); |
| 204 | + if (instrumentation) { |
| 205 | + auto callback = |
| 206 | + base::BindOnce(&CobaltMemoryMetricsEmitter::ReceivedMemoryDump, this); |
| 207 | + std::vector<std::string> mad_list; |
| 208 | + for (const auto& metric : kAllocatorDumpNamesForMetrics) { |
| 209 | + mad_list.push_back(metric.dump_name); |
| 210 | + } |
| 211 | + instrumentation->RequestGlobalDump(mad_list, std::move(callback)); |
| 212 | + } else { |
| 213 | + base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| 214 | + FROM_HERE, |
| 215 | + base::BindOnce(&CobaltMemoryMetricsEmitter::ReceivedMemoryDump, this, |
| 216 | + false, nullptr)); |
| 217 | + } |
| 218 | +} |
| 219 | + |
| 220 | +CobaltMemoryMetricsEmitter::~CobaltMemoryMetricsEmitter() = default; |
| 221 | + |
| 222 | +void CobaltMemoryMetricsEmitter::ReceivedMemoryDump( |
| 223 | + bool success, |
| 224 | + std::unique_ptr<GlobalMemoryDump> dump) { |
| 225 | + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 226 | + |
| 227 | + memory_dump_in_progress_ = false; |
| 228 | + if (!success || !dump) { |
| 229 | + if (callback_for_testing_) { |
| 230 | + std::move(callback_for_testing_).Run(); |
| 231 | + } |
| 232 | + return; |
| 233 | + } |
| 234 | + global_dump_ = std::move(dump); |
| 235 | + CollateResults(); |
| 236 | +} |
| 237 | + |
| 238 | +// static |
| 239 | +void CobaltMemoryMetricsEmitter::EmitProcessUma( |
| 240 | + HistogramProcessType process_type, |
| 241 | + const Metric& item, |
| 242 | + uint64_t value) { |
| 243 | + std::string uma_name = base::StrCat( |
| 244 | + {kExperimentalUmaPrefix, HistogramProcessTypeToString(process_type), |
| 245 | + MetricSizeToVersionSuffix(item.metric_size), item.uma_name}); |
| 246 | + |
| 247 | + switch (item.metric_size) { |
| 248 | + case MetricSize::kPercentage: |
| 249 | + base::UmaHistogramPercentage(uma_name, static_cast<int>(value)); |
| 250 | + break; |
| 251 | + case MetricSize::kLarge: |
| 252 | + base::UmaHistogramMemoryLargeMB(uma_name, static_cast<int>(value / kMiB)); |
| 253 | + break; |
| 254 | + case MetricSize::kSmall: |
| 255 | + base::UmaHistogramMemoryKB(uma_name, static_cast<int>(value / kKiB)); |
| 256 | + break; |
| 257 | + case MetricSize::kTiny: |
| 258 | + base::UmaHistogramCustomCounts(uma_name, static_cast<int>(value), 1, |
| 259 | + 500000, 100); |
| 260 | + break; |
| 261 | + case MetricSize::kCustom: |
| 262 | + base::UmaHistogramCustomCounts(uma_name, static_cast<int>(value), |
| 263 | + item.range.min, item.range.max, 100); |
| 264 | + break; |
| 265 | + } |
| 266 | +} |
| 267 | + |
| 268 | +void CobaltMemoryMetricsEmitter::CollateResults() { |
| 269 | + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 270 | + |
| 271 | + if (memory_dump_in_progress_) { |
| 272 | + return; |
| 273 | + } |
| 274 | + if (!global_dump_) { |
| 275 | + if (callback_for_testing_) { |
| 276 | + std::move(callback_for_testing_).Run(); |
| 277 | + } |
| 278 | + return; |
| 279 | + } |
| 280 | + |
| 281 | + uint64_t private_footprint_total_kb = 0; |
| 282 | + uint64_t shared_footprint_total_kb = 0; |
| 283 | + uint64_t resident_set_total_kb = 0; |
| 284 | + |
| 285 | + for (const auto& pmd : global_dump_->process_dumps()) { |
| 286 | + HistogramProcessType ptype; |
| 287 | + switch (pmd.process_type()) { |
| 288 | + case memory_instrumentation::mojom::ProcessType::BROWSER: |
| 289 | + ptype = HistogramProcessType::kBrowser; |
| 290 | + break; |
| 291 | + case memory_instrumentation::mojom::ProcessType::RENDERER: |
| 292 | + ptype = HistogramProcessType::kRenderer; |
| 293 | + break; |
| 294 | + case memory_instrumentation::mojom::ProcessType::GPU: |
| 295 | + ptype = HistogramProcessType::kGpu; |
| 296 | + break; |
| 297 | + case memory_instrumentation::mojom::ProcessType::UTILITY: |
| 298 | + ptype = HistogramProcessType::kUtility; |
| 299 | + break; |
| 300 | + default: |
| 301 | + ptype = HistogramProcessType::kUtility; |
| 302 | + break; |
| 303 | + } |
| 304 | + |
| 305 | + private_footprint_total_kb += pmd.os_dump().private_footprint_kb; |
| 306 | + shared_footprint_total_kb += pmd.os_dump().shared_footprint_kb; |
| 307 | + resident_set_total_kb += pmd.os_dump().resident_set_kb; |
| 308 | + |
| 309 | + for (const auto& item : kAllocatorDumpNamesForMetrics) { |
| 310 | + absl::optional<uint64_t> value = |
| 311 | + pmd.GetMetric(item.dump_name, item.metric); |
| 312 | + if (value) { |
| 313 | + EmitProcessUma(ptype, item, value.value()); |
| 314 | + } |
| 315 | + } |
| 316 | + |
| 317 | + const char* process_name = HistogramProcessTypeToString(ptype); |
| 318 | + base::UmaHistogramMemoryLargeMB( |
| 319 | + std::string(kMemoryHistogramPrefix) + process_name + ".ResidentSet", |
| 320 | + static_cast<int>(pmd.os_dump().resident_set_kb / kKiB)); |
| 321 | + base::UmaHistogramMemoryLargeMB( |
| 322 | + GetPrivateFootprintHistogramName(ptype), |
| 323 | + static_cast<int>(pmd.os_dump().private_footprint_kb / kKiB)); |
| 324 | + base::UmaHistogramMemoryLargeMB( |
| 325 | + std::string(kMemoryHistogramPrefix) + process_name + |
| 326 | + ".SharedMemoryFootprint", |
| 327 | + static_cast<int>(pmd.os_dump().shared_footprint_kb / kKiB)); |
| 328 | + } |
| 329 | + |
| 330 | + base::UmaHistogramMemoryLargeMB( |
| 331 | + "Memory.Total.ResidentSet", |
| 332 | + static_cast<int>(resident_set_total_kb / kKiB)); |
| 333 | + base::UmaHistogramMemoryLargeMB( |
| 334 | + "Memory.Total.PrivateMemoryFootprint", |
| 335 | + static_cast<int>(private_footprint_total_kb / kKiB)); |
| 336 | + base::UmaHistogramMemoryLargeMB( |
| 337 | + "Memory.Total.SharedMemoryFootprint", |
| 338 | + static_cast<int>(shared_footprint_total_kb / kKiB)); |
| 339 | + |
| 340 | + global_dump_ = nullptr; |
| 341 | + |
| 342 | + if (callback_for_testing_) { |
| 343 | + std::move(callback_for_testing_).Run(); |
| 344 | + } |
| 345 | +} |
| 346 | + |
| 347 | +} // namespace cobalt |
0 commit comments