diff --git a/cobalt/browser/BUILD.gn b/cobalt/browser/BUILD.gn index 0eeff0be6b9b..c35b360df49a 100644 --- a/cobalt/browser/BUILD.gn +++ b/cobalt/browser/BUILD.gn @@ -76,6 +76,7 @@ source_set("browser") { "//components/os_crypt/sync", "//components/prefs", "//components/variations/service:service", + "//services/resource_coordinator/public/cpp/memory_instrumentation", "//starboard:starboard_group", ] diff --git a/cobalt/browser/cobalt_browser_main_parts.cc b/cobalt/browser/cobalt_browser_main_parts.cc index f2edece37057..31cacf26e5e8 100644 --- a/cobalt/browser/cobalt_browser_main_parts.cc +++ b/cobalt/browser/cobalt_browser_main_parts.cc @@ -17,12 +17,16 @@ #include #include "base/path_service.h" +#include "base/trace_event/memory_dump_manager.h" #include "cobalt/browser/global_features.h" #include "cobalt/browser/metrics/cobalt_metrics_service_client.h" #include "cobalt/shell/common/shell_paths.h" #include "components/metrics/metrics_service.h" #include "components/metrics_services_manager/metrics_services_manager.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/resource_coordinator_service.h" +#include "services/resource_coordinator/public/cpp/memory_instrumentation/client_process_impl.h" +#include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h" #if BUILDFLAG(IS_ANDROIDTV) #include "base/android/memory_pressure_listener_android.h" @@ -38,8 +42,48 @@ namespace cobalt { +namespace { + +void InitializeBrowserMemoryInstrumentationClient() { + if (memory_instrumentation::MemoryInstrumentation::GetInstance()) { + return; + } + + auto task_runner = base::trace_event::MemoryDumpManager::GetInstance() + ->GetDumpThreadTaskRunner(); + if (!task_runner->RunsTasksInCurrentSequence()) { + task_runner->PostTask( + FROM_HERE, + base::BindOnce(&InitializeBrowserMemoryInstrumentationClient)); + return; + } + + if (memory_instrumentation::MemoryInstrumentation::GetInstance()) { + return; + } + + // Register the browser process as a memory-instrumentation client. + // This replicates content::InitializeBrowserMemoryInstrumentationClient() + // while avoiding unauthorized header includes. + mojo::PendingRemote coordinator; + mojo::PendingRemote process; + auto process_receiver = process.InitWithNewPipeAndPassReceiver(); + content::GetMemoryInstrumentationRegistry()->RegisterClientProcess( + coordinator.InitWithNewPipeAndPassReceiver(), std::move(process), + memory_instrumentation::mojom::ProcessType::BROWSER, + base::GetCurrentProcId(), /*service_name=*/std::nullopt); + memory_instrumentation::ClientProcessImpl::CreateInstance( + std::move(process_receiver), std::move(coordinator), + /*is_browser_process=*/true); +} + +} // namespace + int CobaltBrowserMainParts::PreCreateThreads() { SetupMetrics(); + + InitializeBrowserMemoryInstrumentationClient(); + #if BUILDFLAG(IS_ANDROIDTV) base::android::MemoryPressureListenerAndroid::Initialize( base::android::AttachCurrentThread()); diff --git a/cobalt/browser/metrics/cobalt_memory_metrics_emitter.cc b/cobalt/browser/metrics/cobalt_memory_metrics_emitter.cc index 4728f0a1a29c..9f140ac4f19b 100644 --- a/cobalt/browser/metrics/cobalt_memory_metrics_emitter.cc +++ b/cobalt/browser/metrics/cobalt_memory_metrics_emitter.cc @@ -64,33 +64,33 @@ const CobaltMemoryMetricsEmitter::Metric kAllocatorDumpNamesForMetrics[] = { CobaltMemoryMetricsEmitter::EmitTo::kSizeInUkmAndUma, {}}, {"blink_objects/Document", - "NumberOfDocuments", + "Tiny.NumberOfDocuments", CobaltMemoryMetricsEmitter::MetricSize::kTiny, MemoryAllocatorDump::kNameObjectCount, CobaltMemoryMetricsEmitter::EmitTo::kCountsInUkmAndSizeInUma, {}}, {"blink_objects/Frame", - "NumberOfFrames", + "Tiny.NumberOfFrames", CobaltMemoryMetricsEmitter::MetricSize::kTiny, MemoryAllocatorDump::kNameObjectCount, CobaltMemoryMetricsEmitter::EmitTo::kCountsInUkmAndSizeInUma, {}}, {"blink_objects/LayoutObject", - "NumberOfLayoutObjects", + "Tiny.NumberOfLayoutObjects", CobaltMemoryMetricsEmitter::MetricSize::kTiny, MemoryAllocatorDump::kNameObjectCount, CobaltMemoryMetricsEmitter::EmitTo::kCountsInUkmAndSizeInUma, {}}, {"blink_objects/Node", - "NumberOfNodes", + "Small.NumberOfNodes", CobaltMemoryMetricsEmitter::MetricSize::kSmall, MemoryAllocatorDump::kNameObjectCount, CobaltMemoryMetricsEmitter::EmitTo::kCountsInUkmAndSizeInUma, {}}, - {"font_caches", - "FontCaches", + {"font_caches/shape_caches", + "Small.FontCaches", CobaltMemoryMetricsEmitter::MetricSize::kSmall, - kEffectiveSize, + "size", CobaltMemoryMetricsEmitter::EmitTo::kSizeInUkmAndUma, {}}, {"java_heap", @@ -100,7 +100,7 @@ const CobaltMemoryMetricsEmitter::Metric kAllocatorDumpNamesForMetrics[] = { CobaltMemoryMetricsEmitter::EmitTo::kSizeInUkmAndUma, {}}, {"leveldatabase", - "LevelDatabase", + "Small.LevelDatabase", CobaltMemoryMetricsEmitter::MetricSize::kSmall, kEffectiveSize, CobaltMemoryMetricsEmitter::EmitTo::kSizeInUkmAndUma, @@ -135,14 +135,20 @@ const CobaltMemoryMetricsEmitter::Metric kAllocatorDumpNamesForMetrics[] = { kEffectiveSize, CobaltMemoryMetricsEmitter::EmitTo::kSizeInUkmAndUma, {}}, + {"skia/sk_glyph_cache", + "Skia.Small.SkGlyphCache", + CobaltMemoryMetricsEmitter::MetricSize::kSmall, + "size", + CobaltMemoryMetricsEmitter::EmitTo::kSizeInUkmAndUma, + {}}, {"sqlite", - "Sqlite", + "Small.Sqlite", CobaltMemoryMetricsEmitter::MetricSize::kSmall, kEffectiveSize, CobaltMemoryMetricsEmitter::EmitTo::kSizeInUkmAndUma, {}}, {"ui", - "UI", + "Small.UI", CobaltMemoryMetricsEmitter::MetricSize::kSmall, kEffectiveSize, CobaltMemoryMetricsEmitter::EmitTo::kSizeInUkmAndUma, @@ -171,8 +177,8 @@ const CobaltMemoryMetricsEmitter::Metric kAllocatorDumpNamesForMetrics[] = { constexpr char kExperimentalUmaPrefix[] = "Memory.Experimental."; constexpr char kVersionSuffixNormal[] = "2."; -constexpr char kVersionSuffixSmall[] = "2.Small."; -constexpr char kVersionSuffixTiny[] = "2.Tiny."; +constexpr char kVersionSuffixSmall[] = "2."; +constexpr char kVersionSuffixTiny[] = "2."; static const char* MetricSizeToVersionSuffix( CobaltMemoryMetricsEmitter::MetricSize size) { diff --git a/cobalt/browser/metrics/cobalt_metrics_browsertest.cc b/cobalt/browser/metrics/cobalt_metrics_browsertest.cc index 892ca83c6241..029ddb02df06 100644 --- a/cobalt/browser/metrics/cobalt_metrics_browsertest.cc +++ b/cobalt/browser/metrics/cobalt_metrics_browsertest.cc @@ -102,6 +102,7 @@ IN_PROC_BROWSER_TEST_F(CobaltMetricsBrowserTest, RecordsMemoryMetrics) { check_histogram("Memory.Experimental.Browser2.V8"); check_histogram("Memory.Experimental.Browser2.V8.AllocatedObjects"); check_histogram("Memory.Experimental.Browser2.Skia"); + check_histogram("Memory.Experimental.Browser2.Skia.Small.SkGlyphCache"); check_histogram("Memory.Experimental.Browser2.Small.FontCaches"); check_histogram("Memory.Experimental.Browser2.Small.LevelDatabase"); check_histogram("Memory.Experimental.Browser2.Small.UI"); diff --git a/cobalt/browser/metrics/cobalt_metrics_service_client_test.cc b/cobalt/browser/metrics/cobalt_metrics_service_client_test.cc index a12ad8172319..e3337be438be 100644 --- a/cobalt/browser/metrics/cobalt_metrics_service_client_test.cc +++ b/cobalt/browser/metrics/cobalt_metrics_service_client_test.cc @@ -81,6 +81,18 @@ class TestProcessMemoryMetricsEmitter : public CobaltMemoryMetricsEmitter { malloc_dump->numeric_entries["allocated_objects_size"] = 8 * 1024 * 1024; browser_dump->chrome_allocator_dumps["malloc"] = std::move(malloc_dump); + // Add Skia Glyph Cache dump + auto skia_dump = memory_instrumentation::mojom::AllocatorMemDump::New(); + skia_dump->numeric_entries["size"] = 2 * 1024 * 1024; + browser_dump->chrome_allocator_dumps["skia/sk_glyph_cache"] = + std::move(skia_dump); + + // Add Font Caches dump + auto font_dump = memory_instrumentation::mojom::AllocatorMemDump::New(); + font_dump->numeric_entries["size"] = 512 * 1024; + browser_dump->chrome_allocator_dumps["font_caches/shape_caches"] = + std::move(font_dump); + dump_ptr->process_dumps.push_back(std::move(browser_dump)); auto global_dump = @@ -368,6 +380,12 @@ TEST_F(CobaltMetricsServiceClientTest, RecordMemoryMetricsRecordsHistogram) { .GetAllSamples("Memory.Experimental.Browser2.Malloc.AllocatedObjects") .size(), 1u); + + // Experimental histograms (now with .Small. suffix in KB) + histogram_tester.ExpectUniqueSample( + "Memory.Experimental.Browser2.Skia.Small.SkGlyphCache", 2048, 1); + histogram_tester.ExpectUniqueSample( + "Memory.Experimental.Browser2.Small.FontCaches", 512, 1); } TEST_F(CobaltMetricsServiceClientTest, diff --git a/cobalt/browser/metrics/font_metrics_browsertest.cc b/cobalt/browser/metrics/font_metrics_browsertest.cc new file mode 100644 index 000000000000..593e3d04db44 --- /dev/null +++ b/cobalt/browser/metrics/font_metrics_browsertest.cc @@ -0,0 +1,100 @@ +// Copyright 2026 The Cobalt Authors. All Rights Reserved. +// +// 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. + +#include "base/run_loop.h" +#include "base/test/metrics/histogram_tester.h" +#include "base/threading/thread_restrictions.h" +#include "cobalt/browser/global_features.h" +#include "cobalt/browser/metrics/cobalt_metrics_service_client.h" +#include "cobalt/browser/metrics/cobalt_metrics_services_manager_client.h" +#include "cobalt/browser/switches.h" +#include "cobalt/testing/browser_tests/browser/test_shell.h" +#include "cobalt/testing/browser_tests/content_browser_test.h" +#include "components/metrics/metrics_pref_names.h" +#include "components/metrics_services_manager/metrics_services_manager.h" +#include "components/prefs/pref_service.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cobalt { + +class FontMetricsBrowserTest : public content::ContentBrowserTest { + public: + FontMetricsBrowserTest() = default; + ~FontMetricsBrowserTest() override = default; + + void SetUpCommandLine(base::CommandLine* command_line) override { + content::ContentBrowserTest::SetUpCommandLine(command_line); + // Set a short interval for memory metrics to verify periodic recording. + command_line->AppendSwitchASCII(switches::kMemoryMetricsInterval, "1"); + } +}; + +IN_PROC_BROWSER_TEST_F(FontMetricsBrowserTest, RecordsFontHistograms) { + base::HistogramTester histogram_tester; + + base::ScopedAllowBlockingForTesting allow_blocking; + auto* features = GlobalFeatures::GetInstance(); + + // Ensure metrics recording is started. + features->metrics_services_manager()->UpdateUploadPermissions(true); + + // Load a page with various characters to exercise font caches and fallback in + // Blink. + std::string html_content = R"( + + +

Standard Latin text.

+

Emoji: 😀 😃 😄 😁 😆 😅 😂 🤣 🦩 🦒 🦚 🦝

+

Rare CJK: 𠜎 𠜱 𠝹 𠱓 𠱸 𠲖 𠳏 𠳕

+ + + )"; + + GURL url("data:text/html;charset=utf-8," + html_content); + ASSERT_TRUE(content::NavigateToURL(shell()->web_contents(), url)); + + // Trigger a memory dump manually for testing. + auto* manager_client = features->metrics_services_manager_client(); + ASSERT_TRUE(manager_client); + auto* client = static_cast( + manager_client->metrics_service_client()); + ASSERT_TRUE(client); + + base::RunLoop run_loop; + client->ScheduleRecordForTesting(run_loop.QuitClosure()); + run_loop.Run(); + + base::RunLoop().RunUntilIdle(); + + // Check for memory histograms (Skia Glyph Cache). + EXPECT_GE( + histogram_tester.GetAllSamples("Memory.Browser.PrivateMemoryFootprint") + .size(), + 1u); + + EXPECT_GE( + histogram_tester + .GetAllSamples("Memory.Experimental.Browser2.Skia.Small.SkGlyphCache") + .size(), + 1u); + + EXPECT_GE(histogram_tester + .GetAllSamples("Memory.Experimental.Browser2.Small.FontCaches") + .size(), + 1u); +} + +} // namespace cobalt diff --git a/cobalt/docs/font_metrics.md b/cobalt/docs/font_metrics.md new file mode 100644 index 000000000000..4f4db464a407 --- /dev/null +++ b/cobalt/docs/font_metrics.md @@ -0,0 +1,60 @@ +# Font Resource and Memory Usage Metrics in Cobalt + +This document summarizes the UMA histograms, Memory Infra dumps, and performance metrics used to track font, glyph cache, and text rendering resource usage in Cobalt. + +## 1. Memory Consumption (Memory Infra / UMA) + +These metrics track actual bytes used in RAM and GPU memory. + +### Skia Graphics Layer +* **Skia Glyph Cache** + * **Dump Path**: `skia/sk_glyph_cache` + * **UMA Mapping**: `Memory.Experimental.[Process].Skia.SkGlyphCache` + * **Description**: Tracks the memory used by Skia's internal "Strike" cache (rasterized glyph masks). + * **Source**: `skia/ext/skia_memory_dump_provider.cc` via `SkGraphics::GetFontCacheUsed()`. + * **Cobalt Status**: Enabled in `cobalt/tools/uma/memory_histograms.txt`. + +* **GPU Glyph Atlas** + * **Dump Path**: `skia/gpu_resources/glyph_atlas` (in Chromium/Skia) + * **UMA Mapping**: `Memory.Gpu.Skia.GpuResources.GlyphAtlas` + * **Description**: Memory used by the GPU texture atlas where glyphs are packed for hardware-accelerated rendering. + +### Blink / Renderer Layer +* **Font Shape Caches** + * **Dump Path**: `font_caches/shape_caches` + * **UMA Mapping**: `Memory.Experimental.[Process].FontCaches` + * **Description**: Memory used for caching text shaping results (the output of HarfBuzz). + * **Source**: `third_party/blink/renderer/platform/fonts/font_cache.cc`. + +* **Font Platform Data Cache** + * **Dump Path**: `font_caches/font_platform_data_cache` + * **Description**: Tracks memory used by the cache of platform-specific font handles. + * **Allowlist**: Included in `base/trace_event/memory_infra_background_allowlist.cc`. + +## 2. Resource Efficiency & Cache Performance + +* **RenderTextHarfBuzz.ShapeRunsFallback** + * **Type**: Enumeration + * **Description**: Tracks how often font fallback is triggered during the text shaping phase. + * **Source**: `ui/gfx/render_text_harfbuzz.cc`. + +* **RenderTextHarfBuzz.GetFallbackFontTime** + * **Type**: Times (ms) + * **Description**: Time spent searching for a suitable fallback font when a character is missing. + * **Source**: `ui/gfx/render_text_harfbuzz.cc`. + +* **Blink.Fonts.DecodeTime** + * **Type**: Microseconds + * **Description**: Time spent decoding font resources. + * **Source**: `third_party/blink/renderer/platform/fonts/font_resource.cc`. + +## 3. Cobalt Integration Details + +* **Metrics Management**: `cobalt/browser/metrics/cobalt_metrics_service_client.cc` coordinates global memory dumps. +* **Allowlist**: Cobalt-specific histogram reporting is controlled by `cobalt/tools/uma/memory_histograms.txt`. + +## How to Verify in Cobalt + +1. **Memory Dumps**: Trigger a global memory dump via CDP or the internal metrics service to see the `skia/sk_glyph_cache` and `font_caches/*` breakdown. +2. **UMA Verification**: Use `cobalt/tools/uma/pull_uma_histogram_set_via_cdp.py` to fetch currently recorded histograms from a running Cobalt instance and verify font-related entries. +3. **Unit Tests**: Exercise `ui/gfx/render_text_unittest.cc` or similar tests to see histogram emission in a controlled environment. diff --git a/cobalt/testing/browser_tests/BUILD.gn b/cobalt/testing/browser_tests/BUILD.gn index 4cbb25fc7964..63e5153118fe 100644 --- a/cobalt/testing/browser_tests/BUILD.gn +++ b/cobalt/testing/browser_tests/BUILD.gn @@ -209,6 +209,7 @@ test("cobalt_browsertests") { sources = [ # TODO(b/458665232): restore browser tests in M138. "//cobalt/browser/metrics/cobalt_metrics_browsertest.cc", + "//cobalt/browser/metrics/font_metrics_browsertest.cc", "content_main_runner_impl_browsertest.cc", #"encrypted_media_browsertest.cc", diff --git a/cobalt/tools/uma/memory_histograms.txt b/cobalt/tools/uma/memory_histograms.txt index 2adf922e601a..232745f72d11 100644 --- a/cobalt/tools/uma/memory_histograms.txt +++ b/cobalt/tools/uma/memory_histograms.txt @@ -176,8 +176,9 @@ Memory.Experimental.Browser2.SiteStorage Memory.Experimental.Browser2.SiteStorage.BlobStorage Memory.Experimental.Browser2.Skia Memory.Experimental.Browser2.Skia.PurgeableSize -Memory.Experimental.Browser2.Skia.SkGlyphCache +Memory.Experimental.Browser2.Skia.Small.SkGlyphCache Memory.Experimental.Browser2.Skia.SkResourceCache +Memory.Experimental.Browser2.Small.FontCaches Memory.Experimental.Browser2.Sync Memory.Experimental.Browser2.V8 Memory.Experimental.Browser2.V8.AllocatedObjects @@ -324,7 +325,7 @@ Memory.Experimental.Extension2.SiteStorage Memory.Experimental.Extension2.SiteStorage.BlobStorage Memory.Experimental.Extension2.Skia Memory.Experimental.Extension2.Skia.PurgeableSize -Memory.Experimental.Extension2.Skia.SkGlyphCache +Memory.Experimental.Extension2.Skia.Small.SkGlyphCache Memory.Experimental.Extension2.Skia.SkResourceCache Memory.Experimental.Extension2.Sync Memory.Experimental.Extension2.V8 @@ -518,8 +519,9 @@ Memory.Experimental.Gpu2.SiteStorage Memory.Experimental.Gpu2.SiteStorage.BlobStorage Memory.Experimental.Gpu2.Skia Memory.Experimental.Gpu2.Skia.PurgeableSize -Memory.Experimental.Gpu2.Skia.SkGlyphCache +Memory.Experimental.Gpu2.Skia.Small.SkGlyphCache Memory.Experimental.Gpu2.Skia.SkResourceCache +Memory.Experimental.Gpu2.Small.FontCaches Memory.Experimental.Gpu2.Sync Memory.Experimental.Gpu2.V8 Memory.Experimental.Gpu2.V8.AllocatedObjects @@ -668,7 +670,7 @@ Memory.Experimental.NetworkService2.SiteStorage Memory.Experimental.NetworkService2.SiteStorage.BlobStorage Memory.Experimental.NetworkService2.Skia Memory.Experimental.NetworkService2.Skia.PurgeableSize -Memory.Experimental.NetworkService2.Skia.SkGlyphCache +Memory.Experimental.NetworkService2.Skia.Small.SkGlyphCache Memory.Experimental.NetworkService2.Skia.SkResourceCache Memory.Experimental.NetworkService2.Sync Memory.Experimental.NetworkService2.V8 @@ -822,8 +824,9 @@ Memory.Experimental.Renderer2.SiteStorage Memory.Experimental.Renderer2.SiteStorage.BlobStorage Memory.Experimental.Renderer2.Skia Memory.Experimental.Renderer2.Skia.PurgeableSize -Memory.Experimental.Renderer2.Skia.SkGlyphCache +Memory.Experimental.Renderer2.Skia.Small.SkGlyphCache Memory.Experimental.Renderer2.Skia.SkResourceCache +Memory.Experimental.Renderer2.Small.FontCaches Memory.Experimental.Renderer2.Sync Memory.Experimental.Renderer2.V8 Memory.Experimental.Renderer2.V8.AllocatedObjects @@ -970,7 +973,7 @@ Memory.Experimental.Utility2.SiteStorage Memory.Experimental.Utility2.SiteStorage.BlobStorage Memory.Experimental.Utility2.Skia Memory.Experimental.Utility2.Skia.PurgeableSize -Memory.Experimental.Utility2.Skia.SkGlyphCache +Memory.Experimental.Utility2.Skia.Small.SkGlyphCache Memory.Experimental.Utility2.Skia.SkResourceCache Memory.Experimental.Utility2.Sync Memory.Experimental.Utility2.V8