blob: e37f3d84356bf871986131712d60369ba073bba5 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/gpu/peak_gpu_memory_tracker_impl.h"
#include <memory>
#include "base/bind.h"
#include "base/containers/flat_map.h"
#include "base/location.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/strcat.h"
#include "base/task/post_task.h"
#include "base/threading/thread_task_runner_handle.h"
#include "content/browser/gpu/gpu_process_host.h"
#include "content/public/browser/gpu_data_manager.h"
#include "services/viz/privileged/mojom/gl/gpu_service.mojom.h"
namespace content {
namespace {
// These count values should be recalculated in case of changes to the number
// of values in their respective enums.
constexpr int kUsageTypeCount =
static_cast<int>(PeakGpuMemoryTracker::Usage::USAGE_MAX) + 1;
constexpr int kAllocationSourceTypeCount =
static_cast<int>(gpu::GpuPeakMemoryAllocationSource::
GPU_PEAK_MEMORY_ALLOCATION_SOURCE_MAX) +
1;
constexpr int kAllocationSourceHistogramIndex =
kUsageTypeCount * kAllocationSourceTypeCount;
// Histogram values based on UMA_HISTOGRAM_MEMORY_KB
constexpr int kMemoryHistogramMin = 1000;
constexpr int kMemoryHistogramMax = 500000;
constexpr int kMemoryHistogramBucketCount = 50;
constexpr const char* GetUsageName(PeakGpuMemoryTracker::Usage usage) {
switch (usage) {
case PeakGpuMemoryTracker::Usage::CHANGE_TAB:
return "ChangeTab";
case PeakGpuMemoryTracker::Usage::PAGE_LOAD:
return "PageLoad";
case PeakGpuMemoryTracker::Usage::SCROLL:
return "Scroll";
}
}
constexpr const char* GetAllocationSourceName(
gpu::GpuPeakMemoryAllocationSource source) {
switch (source) {
case gpu::GpuPeakMemoryAllocationSource::UNKNOWN:
return "Unknown";
case gpu::GpuPeakMemoryAllocationSource::COMMAND_BUFFER:
return "CommandBuffer";
case gpu::GpuPeakMemoryAllocationSource::SHARED_CONTEXT_STATE:
return "SharedContextState";
case gpu::GpuPeakMemoryAllocationSource::SHARED_IMAGE_STUB:
return "SharedImageStub";
case gpu::GpuPeakMemoryAllocationSource::SKIA:
return "Skia";
}
}
std::string GetPeakMemoryUsageUMAName(PeakGpuMemoryTracker::Usage usage) {
return base::StrCat({"Memory.GPU.PeakMemoryUsage.", GetUsageName(usage)});
}
std::string GetPeakMemoryAllocationSourceUMAName(
PeakGpuMemoryTracker::Usage usage,
gpu::GpuPeakMemoryAllocationSource source) {
return base::StrCat({"Memory.GPU.PeakMemoryAllocationSource.",
GetUsageName(usage), ".",
GetAllocationSourceName(source)});
}
// Callback provided to the GpuService, which will be notified of the
// |peak_memory| used. This will then report that to UMA Histograms, for the
// requested |usage|. Some tests may provide an optional |testing_callback| in
// order to sync tests with the work done here on the IO thread.
void PeakMemoryCallback(PeakGpuMemoryTracker::Usage usage,
base::OnceClosure testing_callback,
const uint64_t peak_memory,
const base::flat_map<gpu::GpuPeakMemoryAllocationSource,
uint64_t>& allocation_per_source) {
uint64_t memory_in_kb = peak_memory / 1024u;
STATIC_HISTOGRAM_POINTER_GROUP(
GetPeakMemoryUsageUMAName(usage), static_cast<int>(usage),
kUsageTypeCount, Add(memory_in_kb),
base::Histogram::FactoryGet(
GetPeakMemoryUsageUMAName(usage), kMemoryHistogramMin,
kMemoryHistogramMax, kMemoryHistogramBucketCount,
base::HistogramBase::kUmaTargetedHistogramFlag));
for (auto& source : allocation_per_source) {
uint64_t source_memory_in_kb = source.second / 1024u;
STATIC_HISTOGRAM_POINTER_GROUP(
GetPeakMemoryAllocationSourceUMAName(usage, source.first),
static_cast<int>(usage) * kAllocationSourceTypeCount +
static_cast<int>(source.first),
kAllocationSourceHistogramIndex, Add(source_memory_in_kb),
base::Histogram::FactoryGet(
GetPeakMemoryAllocationSourceUMAName(usage, source.first),
kMemoryHistogramMin, kMemoryHistogramMax,
kMemoryHistogramBucketCount,
base::HistogramBase::kUmaTargetedHistogramFlag));
}
std::move(testing_callback).Run();
}
} // namespace
// static
std::unique_ptr<PeakGpuMemoryTracker> PeakGpuMemoryTracker::Create(
PeakGpuMemoryTracker::Usage usage) {
return std::make_unique<PeakGpuMemoryTrackerImpl>(usage);
}
// static
uint32_t PeakGpuMemoryTrackerImpl::next_sequence_number_ = 0;
PeakGpuMemoryTrackerImpl::PeakGpuMemoryTrackerImpl(
PeakGpuMemoryTracker::Usage usage)
: usage_(usage) {
// Actually performs request to GPU service to begin memory tracking for
// |sequence_number_|. This will normally be created from the UI thread, so
// repost to the IO thread.
GpuProcessHost::CallOnIO(
GPU_PROCESS_KIND_SANDBOXED, /* force_create=*/false,
base::BindOnce(
[](uint32_t sequence_num, GpuProcessHost* host) {
// There may be no host nor service available. This may occur during
// shutdown, when the service is fully disabled, and in some tests.
// In those cases do nothing.
if (!host)
return;
if (auto* gpu_service = host->gpu_service()) {
gpu_service->StartPeakMemoryMonitor(sequence_num);
}
},
sequence_num_));
}
PeakGpuMemoryTrackerImpl::~PeakGpuMemoryTrackerImpl() {
if (canceled_)
return;
GpuProcessHost::CallOnIO(
GPU_PROCESS_KIND_SANDBOXED, /* force_create=*/false,
base::BindOnce(
[](uint32_t sequence_num, PeakGpuMemoryTracker::Usage usage,
base::OnceClosure testing_callback, GpuProcessHost* host) {
// There may be no host nor service available. This may occur during
// shutdown, when the service is fully disabled, and in some tests.
// In those cases there is nothing to report to UMA. However we
// still run the optional testing callback.
if (!host) {
std::move(testing_callback).Run();
return;
}
if (auto* gpu_service = host->gpu_service()) {
gpu_service->GetPeakMemoryUsage(
sequence_num, base::BindOnce(&PeakMemoryCallback, usage,
std::move(testing_callback)));
}
},
sequence_num_, usage_,
std::move(post_gpu_service_callback_for_testing_)));
}
void PeakGpuMemoryTrackerImpl::Cancel() {
canceled_ = true;
// Notify the GpuProcessHost that we are done observing this sequence.
GpuProcessHost::CallOnIO(GPU_PROCESS_KIND_SANDBOXED, /* force_create=*/false,
base::BindOnce(
[](uint32_t sequence_num, GpuProcessHost* host) {
if (!host)
return;
if (auto* gpu_service = host->gpu_service())
gpu_service->GetPeakMemoryUsage(
sequence_num, base::DoNothing());
},
sequence_num_));
}
} // namespace content