mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-26 20:30:41 +00:00
Bug 1492930 - Part 4. Add ImageMemoryReporter to support extra shared surfaces reporting. r=tnikkel
By delegating responsibility for shared surfaces reporting to imagelib, we can cross reference the GPU shared surfaces cache with the local surface cache in a content process (or the main process). This will allow us to identify entries that are in the GPU cache but not in the content/main process cache, and aid in debugging memory leaks. This functionality is pref'd off by default behind image.mem.debug-reporting. Additionally, we want to report every entry that was mapped into the compositor process, in the compositor process memory report. This will give us a sense of how much of our resident memory is consumed by mapped images in absence of the more detailed cross referencing above.
This commit is contained in:
parent
d49646bb7a
commit
5d483b60ce
@ -557,6 +557,7 @@ private:
|
||||
DECL_GFX_PREF(Live, "image.mem.discardable", ImageMemDiscardable, bool, false);
|
||||
DECL_GFX_PREF(Once, "image.mem.animated.discardable", ImageMemAnimatedDiscardable, bool, false);
|
||||
DECL_GFX_PREF(Live, "image.mem.animated.use_heap", ImageMemAnimatedUseHeap, bool, false);
|
||||
DECL_GFX_PREF(Live, "image.mem.debug-reporting", ImageMemDebugReporting, bool, false);
|
||||
DECL_GFX_PREF(Live, "image.mem.shared", ImageMemShared, bool, true);
|
||||
DECL_GFX_PREF(Once, "image.mem.surfacecache.discard_factor", ImageMemSurfaceCacheDiscardFactor, uint32_t, 1);
|
||||
DECL_GFX_PREF(Once, "image.mem.surfacecache.max_size_kb", ImageMemSurfaceCacheMaxSizeKB, uint32_t, 100 * 1024);
|
||||
|
187
image/ImageMemoryReporter.cpp
Normal file
187
image/ImageMemoryReporter.cpp
Normal file
@ -0,0 +1,187 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ImageMemoryReporter.h"
|
||||
#include "Image.h"
|
||||
#include "mozilla/layers/SharedSurfacesParent.h"
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
|
||||
ImageMemoryReporter::WebRenderReporter* ImageMemoryReporter::sWrReporter;
|
||||
|
||||
class ImageMemoryReporter::WebRenderReporter final : public nsIMemoryReporter
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
WebRenderReporter()
|
||||
{ }
|
||||
|
||||
NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aData, bool aAnonymize) override
|
||||
{
|
||||
layers::SharedSurfacesMemoryReport report;
|
||||
layers::SharedSurfacesParent::AccumulateMemoryReport(report);
|
||||
ReportSharedSurfaces(aHandleReport, aData, /* aIsForCompositor */ true, report);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual ~WebRenderReporter()
|
||||
{ }
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(ImageMemoryReporter::WebRenderReporter, nsIMemoryReporter)
|
||||
|
||||
/* static */ void
|
||||
ImageMemoryReporter::InitForWebRender()
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess() || XRE_IsGPUProcess());
|
||||
if (!sWrReporter) {
|
||||
sWrReporter = new WebRenderReporter();
|
||||
RegisterStrongMemoryReporter(sWrReporter);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ImageMemoryReporter::ShutdownForWebRender()
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess() || XRE_IsGPUProcess());
|
||||
if (sWrReporter) {
|
||||
UnregisterStrongMemoryReporter(sWrReporter);
|
||||
sWrReporter = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ImageMemoryReporter::ReportSharedSurfaces(nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aData,
|
||||
const layers::SharedSurfacesMemoryReport& aSharedSurfaces)
|
||||
{
|
||||
ReportSharedSurfaces(aHandleReport, aData,
|
||||
/* aIsForCompositor */ false,
|
||||
aSharedSurfaces);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ImageMemoryReporter::ReportSharedSurfaces(nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aData,
|
||||
bool aIsForCompositor,
|
||||
const layers::SharedSurfacesMemoryReport& aSharedSurfaces)
|
||||
{
|
||||
MOZ_ASSERT_IF(aIsForCompositor, XRE_IsParentProcess() || XRE_IsGPUProcess());
|
||||
MOZ_ASSERT_IF(!aIsForCompositor,
|
||||
XRE_IsParentProcess() || XRE_IsContentProcess());
|
||||
|
||||
for (auto i = aSharedSurfaces.mSurfaces.begin();
|
||||
i != aSharedSurfaces.mSurfaces.end(); ++i) {
|
||||
ReportSharedSurface(aHandleReport, aData, aIsForCompositor, i->first, i->second);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ImageMemoryReporter::ReportSharedSurface(nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aData,
|
||||
bool aIsForCompositor,
|
||||
uint64_t aExternalId,
|
||||
const layers::SharedSurfacesMemoryReport::SurfaceEntry& aEntry)
|
||||
{
|
||||
nsAutoCString path;
|
||||
if (aIsForCompositor) {
|
||||
path.AppendLiteral("gfx/webrender/images/mapped_from_owner/");
|
||||
} else {
|
||||
path.AppendLiteral("gfx/webrender/images/owner_cache_missing/");
|
||||
}
|
||||
|
||||
if (aIsForCompositor) {
|
||||
path.AppendLiteral("pid=");
|
||||
path.AppendInt(aEntry.mCreatorPid);
|
||||
path.AppendLiteral("/");
|
||||
}
|
||||
|
||||
if (gfxPrefs::ImageMemDebugReporting()) {
|
||||
path.AppendPrintf("%016lx/", aExternalId);
|
||||
}
|
||||
|
||||
path.AppendLiteral("image(");
|
||||
path.AppendInt(aEntry.mSize.width);
|
||||
path.AppendLiteral("x");
|
||||
path.AppendInt(aEntry.mSize.height);
|
||||
path.AppendLiteral(", compositor_ref:");
|
||||
path.AppendInt(aEntry.mConsumers);
|
||||
path.AppendLiteral(", creator_ref:");
|
||||
path.AppendInt(aEntry.mCreatorRef);
|
||||
path.AppendLiteral(")/decoded-nonheap");
|
||||
|
||||
size_t surfaceSize =
|
||||
mozilla::ipc::SharedMemory::PageAlignedSize(aEntry.mSize.height *
|
||||
aEntry.mStride);
|
||||
|
||||
// If this memory has already been reported elsewhere (e.g. as part of our
|
||||
// explicit section in the surface cache), we don't want report it again as
|
||||
// KIND_NONHEAP and have it counted again.
|
||||
bool sameProcess = aEntry.mCreatorPid == base::GetCurrentProcId();
|
||||
int32_t kind = aIsForCompositor && !sameProcess
|
||||
? nsIMemoryReporter::KIND_NONHEAP
|
||||
: nsIMemoryReporter::KIND_OTHER;
|
||||
|
||||
NS_NAMED_LITERAL_CSTRING(desc, "Decoded image data stored in shared memory.");
|
||||
aHandleReport->Callback(EmptyCString(), path, kind,
|
||||
nsIMemoryReporter::UNITS_BYTES,
|
||||
surfaceSize, desc, aData);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ImageMemoryReporter::AppendSharedSurfacePrefix(nsACString& aPathPrefix,
|
||||
const SurfaceMemoryCounter& aCounter,
|
||||
layers::SharedSurfacesMemoryReport& aSharedSurfaces)
|
||||
{
|
||||
uint64_t extId = aCounter.Values().ExternalId();
|
||||
if (extId) {
|
||||
auto gpuEntry = aSharedSurfaces.mSurfaces.find(extId);
|
||||
|
||||
if (gfxPrefs::ImageMemDebugReporting()) {
|
||||
aPathPrefix.AppendPrintf(", external_id:%016lx", extId);
|
||||
if (gpuEntry != aSharedSurfaces.mSurfaces.end()) {
|
||||
aPathPrefix.AppendLiteral(", compositor_ref:");
|
||||
aPathPrefix.AppendInt(gpuEntry->second.mConsumers);
|
||||
} else {
|
||||
aPathPrefix.AppendLiteral(", compositor_ref:missing");
|
||||
}
|
||||
}
|
||||
|
||||
if (gpuEntry != aSharedSurfaces.mSurfaces.end()) {
|
||||
MOZ_ASSERT(gpuEntry->second.mCreatorRef);
|
||||
aSharedSurfaces.mSurfaces.erase(gpuEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
ImageMemoryReporter::TrimSharedSurfaces(const ImageMemoryCounter& aCounter,
|
||||
layers::SharedSurfacesMemoryReport& aSharedSurfaces)
|
||||
{
|
||||
if (aSharedSurfaces.mSurfaces.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const SurfaceMemoryCounter& counter : aCounter.Surfaces()) {
|
||||
uint64_t extId = counter.Values().ExternalId();
|
||||
if (extId) {
|
||||
auto gpuEntry = aSharedSurfaces.mSurfaces.find(extId);
|
||||
if (gpuEntry != aSharedSurfaces.mSurfaces.end()) {
|
||||
MOZ_ASSERT(gpuEntry->second.mCreatorRef);
|
||||
aSharedSurfaces.mSurfaces.erase(gpuEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // image
|
||||
} // mozilla
|
96
image/ImageMemoryReporter.h
Normal file
96
image/ImageMemoryReporter.h
Normal file
@ -0,0 +1,96 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_image_ImageMemoryReporter_h
|
||||
#define mozilla_image_ImageMemoryReporter_h
|
||||
|
||||
#include <cstdint>
|
||||
#include "nsString.h"
|
||||
#include "mozilla/layers/SharedSurfacesMemoryReport.h"
|
||||
|
||||
class nsISupports;
|
||||
class nsIHandleReportCallback;
|
||||
|
||||
namespace mozilla {
|
||||
namespace image {
|
||||
struct ImageMemoryCounter;
|
||||
struct SurfaceMemoryCounter;
|
||||
|
||||
class ImageMemoryReporter final
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Initializes image related memory reporting in the compositor process when
|
||||
* using WebRender.
|
||||
*/
|
||||
static void InitForWebRender();
|
||||
|
||||
/**
|
||||
* Tears down image related memory reporting in the compositor process when
|
||||
* using WebRender.
|
||||
*/
|
||||
static void ShutdownForWebRender();
|
||||
|
||||
/**
|
||||
* Report all remaining entries in the shared surface's memory report. This
|
||||
* should be used by the content or main process to allow reporting any
|
||||
* entries that is was unable to cross reference with the local surface cache.
|
||||
* These are candidates for having been leaked. This should be used in
|
||||
* conjunction with AppendSharedSurfacePrefix and/or TrimSharedSurfaces to
|
||||
* produce the expected result.
|
||||
*/
|
||||
static void ReportSharedSurfaces(nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aData,
|
||||
const layers::SharedSurfacesMemoryReport& aSharedSurfaces);
|
||||
|
||||
/**
|
||||
* Adjust the path prefix for a surface to include any additional metadata for
|
||||
* the shared surface, if any. It will also remove any corresponding entries
|
||||
* in the given memory report.
|
||||
*/
|
||||
static void AppendSharedSurfacePrefix(nsACString& aPathPrefix,
|
||||
const SurfaceMemoryCounter& aCounter,
|
||||
layers::SharedSurfacesMemoryReport& aSharedSurfaces);
|
||||
|
||||
/**
|
||||
* Remove all entries in the memory report for the given set of surfaces for
|
||||
* an image. This is useful when we aren't reporting on a particular image
|
||||
* because it isn't notable.
|
||||
*/
|
||||
static void TrimSharedSurfaces(const ImageMemoryCounter& aCounter,
|
||||
layers::SharedSurfacesMemoryReport& aSharedSurfaces);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Report all remaining entries in the shared surface's memory report.
|
||||
*
|
||||
* aIsForCompositor controls how to intepret what remains in the report. If
|
||||
* true, this should mirror exactly what is currently in
|
||||
* SharedSurfacesParent's cache. This will report entries that are currently
|
||||
* mapped into the compositor process. If false, then we are in a content or
|
||||
* main process, and it should have removed entries that also exist in its
|
||||
* local surface cache -- thus any remaining entries are those that are
|
||||
* candidates for leaks.
|
||||
*/
|
||||
static void ReportSharedSurfaces(nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aData,
|
||||
bool aIsForCompositor,
|
||||
const layers::SharedSurfacesMemoryReport& aSharedSurfaces);
|
||||
|
||||
static void ReportSharedSurface(nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aData,
|
||||
bool aIsForCompositor,
|
||||
uint64_t aExternalId,
|
||||
const layers::SharedSurfacesMemoryReport::SurfaceEntry& aEntry);
|
||||
|
||||
class WebRenderReporter;
|
||||
static WebRenderReporter* sWrReporter;
|
||||
};
|
||||
|
||||
} // image
|
||||
} // mozilla
|
||||
|
||||
#endif // mozilla_image_ImageMemoryReporter_h
|
@ -53,6 +53,10 @@ EXPORTS += [
|
||||
'SurfaceCacheUtils.h',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.image += [
|
||||
'ImageMemoryReporter.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'AnimationFrameBuffer.cpp',
|
||||
'AnimationSurfaceProvider.cpp',
|
||||
@ -68,6 +72,7 @@ UNIFIED_SOURCES += [
|
||||
'Image.cpp',
|
||||
'ImageCacheKey.cpp',
|
||||
'ImageFactory.cpp',
|
||||
'ImageMemoryReporter.cpp',
|
||||
'ImageOps.cpp',
|
||||
'ImageWrapper.cpp',
|
||||
'imgFrame.cpp',
|
||||
|
@ -4675,6 +4675,9 @@ pref("image.mem.animated.use_heap", true);
|
||||
pref("image.mem.animated.use_heap", false);
|
||||
#endif
|
||||
|
||||
// Enable extra information for debugging in the image memory reports.
|
||||
pref("image.mem.debug-reporting", false);
|
||||
|
||||
// Decodes images into shared memory to allow direct use in separate
|
||||
// rendering processes. Only applicable with WebRender.
|
||||
pref("image.mem.shared", true);
|
||||
|
Loading…
x
Reference in New Issue
Block a user