Bug 1268021 - Implement memory reporting for the user-font cache. r=njn

This commit is contained in:
Jonathan Kew 2016-05-03 12:14:34 +01:00
parent dac4222c8f
commit fb5e0a3be3
4 changed files with 162 additions and 3 deletions

View File

@ -99,7 +99,8 @@ gfxFontEntry::gfxFontEntry() :
mUnitsPerEm(0),
mHBFace(nullptr),
mGrFace(nullptr),
mGrFaceRefCnt(0)
mGrFaceRefCnt(0),
mComputedSizeOfUserFont(0)
{
memset(&mDefaultSubSpaceFeatures, 0, sizeof(mDefaultSubSpaceFeatures));
memset(&mNonDefaultSubSpaceFeatures, 0, sizeof(mNonDefaultSubSpaceFeatures));
@ -138,7 +139,8 @@ gfxFontEntry::gfxFontEntry(const nsAString& aName, bool aIsStandardFace) :
mUnitsPerEm(0),
mHBFace(nullptr),
mGrFace(nullptr),
mGrFaceRefCnt(0)
mGrFaceRefCnt(0),
mComputedSizeOfUserFont(0)
{
memset(&mDefaultSubSpaceFeatures, 0, sizeof(mDefaultSubSpaceFeatures));
memset(&mNonDefaultSubSpaceFeatures, 0, sizeof(mNonDefaultSubSpaceFeatures));
@ -1146,6 +1148,32 @@ gfxFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
}
// This is used to report the size of an individual downloaded font in the
// user font cache. (Fonts that are part of the platform font list accumulate
// their sizes to the font list's reporter using the AddSizeOf... methods
// above.)
size_t
gfxFontEntry::ComputedSizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
{
FontListSizes s = { 0 };
AddSizeOfExcludingThis(aMallocSizeOf, &s);
// When reporting memory used for the main platform font list,
// where we're typically summing the totals for a few hundred font faces,
// we report the fields of FontListSizes separately.
// But for downloaded user fonts, the actual resource data (added below)
// will dominate, and the minor overhead of these pieces isn't worth
// splitting out for an individual font.
size_t result = s.mFontListSize + s.mFontTableCacheSize + s.mCharMapsSize;
if (mIsDataUserFont) {
MOZ_ASSERT(mComputedSizeOfUserFont > 0, "user font with no data?");
result += mComputedSizeOfUserFont;
}
return result;
}
//////////////////////////////////////////////////////////////////////////////
//
// class gfxFontFamily

View File

@ -375,12 +375,17 @@ public:
// the fonts belonging to this font entry.
void NotifyFontDestroyed(gfxFont* aFont);
// For memory reporting
// For memory reporting of the platform font list.
virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const;
virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
FontListSizes* aSizes) const;
// Used for reporting on individual font entries in the user font cache,
// which are not present in the platform font list.
size_t
ComputedSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
// Used when checking for complex script support, to mask off cmap ranges
struct ScriptRange {
uint32_t rangeStart;
@ -453,6 +458,7 @@ protected:
friend class gfxUserFcFontEntry;
friend class gfxFontFamily;
friend class gfxSingleFaceMacFontFamily;
friend class gfxUserFontEntry;
gfxFontEntry();
@ -531,6 +537,12 @@ protected:
static void GrReleaseTable(const void *aAppFaceHandle,
const void *aTableBuffer);
// For memory reporting: size of user-font data belonging to this entry.
// We record this in the font entry because the actual data block may be
// handed over to platform APIs, so that it would become difficult (and
// platform-specific) to measure it directly at report-gathering time.
uint32_t mComputedSizeOfUserFont;
private:
/**
* Font table hashtable, to support GetFontTable for harfbuzz.

View File

@ -602,6 +602,8 @@ gfxUserFontEntry::SetLoadState(UserFontLoadState aLoadState)
mUserFontLoadState = aLoadState;
}
MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(UserFontMallocSizeOfOnAlloc)
bool
gfxUserFontEntry::LoadPlatformFont(const uint8_t* aFontData, uint32_t& aLength)
{
@ -628,6 +630,7 @@ gfxUserFontEntry::LoadPlatformFont(const uint8_t* aFontData, uint32_t& aLength)
// if necessary. The original data in aFontData is left unchanged.
uint32_t saneLen;
uint32_t fontCompressionRatio = 0;
size_t computedSize = 0;
const uint8_t* saneData =
SanitizeOpenTypeData(aFontData, aLength, saneLen, fontType);
if (!saneData) {
@ -651,6 +654,15 @@ gfxUserFontEntry::LoadPlatformFont(const uint8_t* aFontData, uint32_t& aLength)
// arbitrary/malicious data from the web.
gfxFontUtils::GetFullNameFromSFNT(saneData, saneLen,
originalFullName);
// Record size for memory reporting purposes. We measure this now
// because by the time we potentially want to collect reports, this
// data block may have been handed off to opaque OS font APIs that
// don't allow us to retrieve or measure it directly.
// The *OnAlloc function will also tell DMD about this block, as the
// OS font code may hold on to it for an extended period.
computedSize = UserFontMallocSizeOfOnAlloc(saneData);
// Here ownership of saneData is passed to the platform,
// which will delete it when no longer required
fe = gfxPlatform::GetPlatform()->MakePlatformFont(mName,
@ -665,6 +677,8 @@ gfxUserFontEntry::LoadPlatformFont(const uint8_t* aFontData, uint32_t& aLength)
}
if (fe) {
fe->mComputedSizeOfUserFont = computedSize;
// Save a copy of the metadata block (if present) for nsIDOMFontFace
// to use if required. Ownership of the metadata block will be passed
// to the gfxUserFontData record below.
@ -1151,6 +1165,13 @@ gfxUserFontSet::UserFontCache::CacheFont(gfxFontEntry* aFontEntry,
obs->AddObserver(flusher, "last-pb-context-exited", false);
obs->AddObserver(flusher, "xpcom-shutdown", false);
}
// Create and register a memory reporter for sUserFonts.
// This reporter is never unregistered, but that's OK because
// the reporter checks whether sUserFonts is null, so it would
// be safe to call even after UserFontCache::Shutdown has deleted
// the cache.
RegisterStrongMemoryReporter(new MemoryReporter());
}
if (data->mLength) {
@ -1277,6 +1298,89 @@ gfxUserFontSet::UserFontCache::Shutdown()
}
}
MOZ_DEFINE_MALLOC_SIZE_OF(UserFontsMallocSizeOf)
nsresult
gfxUserFontSet::UserFontCache::Entry::ReportMemory(nsIMemoryReporterCallback* aCb,
nsISupports* aClosure,
bool aAnonymize)
{
nsAutoCString path("explicit/gfx/user-fonts/font(");
if (aAnonymize) {
path.AppendPrintf("<anonymized-%p>", this);
} else {
if (mFontEntry) { // this should always be present
NS_ConvertUTF16toUTF8 familyName(mFontEntry->mFamilyName);
path.AppendPrintf("family=%s", familyName.get());
}
if (mURI) {
nsCString spec;
mURI->GetSpec(spec);
spec.ReplaceChar('/', '\\');
// Some fonts are loaded using horrendously-long data: URIs;
// truncate those before reporting them.
bool isData;
if (NS_SUCCEEDED(mURI->SchemeIs("data", &isData)) && isData &&
spec.Length() > 255) {
spec.Truncate(252);
spec.Append("...");
}
path.AppendPrintf(", url=%s", spec.get());
}
if (mPrincipal) {
nsCOMPtr<nsIURI> uri;
mPrincipal->GetURI(getter_AddRefs(uri));
nsCString spec;
uri->GetSpec(spec);
if (!spec.IsEmpty()) {
// Include a clue as to who loaded this resource. (Note that
// because of font entry sharing, other pages may now be using
// this resource, and the original page may not even be loaded
// any longer.)
spec.ReplaceChar('/', '\\');
path.AppendPrintf(", principal=%s", spec.get());
}
}
}
path.Append(')');
return aCb->
Callback(EmptyCString(), path,
nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES,
mFontEntry->ComputedSizeOfExcludingThis(UserFontsMallocSizeOf),
NS_LITERAL_CSTRING("Memory used by @font-face resource."),
aClosure);
}
NS_IMPL_ISUPPORTS(gfxUserFontSet::UserFontCache::MemoryReporter,
nsIMemoryReporter)
NS_IMETHODIMP
gfxUserFontSet::UserFontCache::MemoryReporter::CollectReports(
nsIMemoryReporterCallback* aCb, nsISupports* aClosure, bool aAnonymize)
{
if (!sUserFonts) {
return NS_OK;
}
for (auto it = sUserFonts->Iter(); !it.Done(); it.Next()) {
nsresult rv = it.Get()->ReportMemory(aCb, aClosure, aAnonymize);
if (NS_FAILED(rv)) {
return rv;
}
}
return aCb->
Callback(EmptyCString(),
NS_LITERAL_CSTRING("explicit/gfx/user-fonts/cache-overhead"),
nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES,
sUserFonts->ShallowSizeOfIncludingThis(UserFontsMallocSizeOf),
NS_LITERAL_CSTRING("Memory used by the @font-face cache, "
"not counting the actual font resources."),
aClosure);
}
#ifdef DEBUG_USERFONT_CACHE
void

View File

@ -322,6 +322,17 @@ public:
// Clear everything so that we don't leak URIs and Principals.
static void Shutdown();
// Memory-reporting support.
class MemoryReporter final : public nsIMemoryReporter
{
private:
~MemoryReporter() { }
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIMEMORYREPORTER
};
#ifdef DEBUG_USERFONT_CACHE
// dump contents
static void Dump();
@ -437,6 +448,10 @@ public:
bool IsPersistent() const { return mPersistence == kPersistent; }
bool IsPrivate() const { return mPrivate; }
nsresult ReportMemory(nsIMemoryReporterCallback* aCb,
nsISupports* aClosure,
bool aAnonymize);
#ifdef DEBUG_USERFONT_CACHE
void Dump();
#endif