/* -*- 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 "base/process_util.h" #include "mozilla/layers/SharedSurfacesParent.h" #include "mozilla/StaticPrefs_image.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(uint32_t(aEntry.mCreatorPid)); path.AppendLiteral("/"); } if (StaticPrefs::image_mem_debug_reporting()) { path.AppendInt(aExternalId, 16); path.AppendLiteral("/"); } 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-"); 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. The paths must be different if the // kinds are different to avoid problems when diffing memory reports. bool sameProcess = aEntry.mCreatorPid == base::GetCurrentProcId(); int32_t kind; if (aIsForCompositor && !sameProcess) { path.AppendLiteral("nonheap"); kind = nsIMemoryReporter::KIND_NONHEAP; } else { path.AppendLiteral("other"); kind = nsIMemoryReporter::KIND_OTHER; } constexpr auto desc = "Decoded image data stored in shared memory."_ns; aHandleReport->Callback(""_ns, 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 (StaticPrefs::image_mem_debug_reporting()) { aPathPrefix.AppendLiteral(", external_id:"); aPathPrefix.AppendInt(extId, 16); 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); } } } } } // namespace image } // namespace mozilla