Bug 956961 - Open content processes' DMD log files in the parent process. r=njn

This commit is contained in:
Jed Davis 2014-07-02 11:27:48 -07:00
parent e10417743e
commit adc6a05bd1
11 changed files with 111 additions and 38 deletions

View File

@ -59,6 +59,7 @@
#include "nsIMutable.h"
#include "nsIObserverService.h"
#include "nsIScriptSecurityManager.h"
#include "nsMemoryInfoDumper.h"
#include "nsServiceManagerUtils.h"
#include "nsStyleSheetService.h"
#include "nsXULAppAPI.h"
@ -192,22 +193,22 @@ public:
NS_DECL_ISUPPORTS
MemoryReportRequestChild(uint32_t aGeneration, bool aAnonymize,
const nsAString& aDMDDumpIdent);
const FileDescriptor& aDMDFile);
NS_IMETHOD Run();
private:
virtual ~MemoryReportRequestChild();
uint32_t mGeneration;
bool mAnonymize;
nsString mDMDDumpIdent;
FileDescriptor mDMDFile;
};
NS_IMPL_ISUPPORTS(MemoryReportRequestChild, nsIRunnable)
MemoryReportRequestChild::MemoryReportRequestChild(
uint32_t aGeneration, bool aAnonymize, const nsAString& aDMDDumpIdent)
uint32_t aGeneration, bool aAnonymize, const FileDescriptor& aDMDFile)
: mGeneration(aGeneration), mAnonymize(aAnonymize),
mDMDDumpIdent(aDMDDumpIdent)
mDMDFile(aDMDFile)
{
MOZ_COUNT_CTOR(MemoryReportRequestChild);
}
@ -692,10 +693,10 @@ PMemoryReportRequestChild*
ContentChild::AllocPMemoryReportRequestChild(const uint32_t& aGeneration,
const bool &aAnonymize,
const bool &aMinimizeMemoryUsage,
const nsString& aDMDDumpIdent)
const FileDescriptor& aDMDFile)
{
MemoryReportRequestChild *actor =
new MemoryReportRequestChild(aGeneration, aAnonymize, aDMDDumpIdent);
new MemoryReportRequestChild(aGeneration, aAnonymize, aDMDFile);
actor->AddRef();
return actor;
}
@ -750,7 +751,7 @@ ContentChild::RecvPMemoryReportRequestConstructor(
const uint32_t& aGeneration,
const bool& aAnonymize,
const bool& aMinimizeMemoryUsage,
const nsString& aDMDDumpIdent)
const FileDescriptor& aDMDFile)
{
MemoryReportRequestChild *actor =
static_cast<MemoryReportRequestChild*>(aChild);
@ -784,7 +785,7 @@ NS_IMETHODIMP MemoryReportRequestChild::Run()
new MemoryReportsWrapper(&reports);
nsRefPtr<MemoryReportCallback> cb = new MemoryReportCallback(process);
mgr->GetReportsForThisProcessExtended(cb, wrappedReports, mAnonymize,
mDMDDumpIdent);
FileDescriptorToFILE(mDMDFile, "wb"));
bool sent = Send__delete__(this, mGeneration, reports);
return sent ? NS_OK : NS_ERROR_FAILURE;

View File

@ -155,7 +155,7 @@ public:
AllocPMemoryReportRequestChild(const uint32_t& aGeneration,
const bool& aAnonymize,
const bool& aMinimizeMemoryUsage,
const nsString& aDMDDumpIdent) MOZ_OVERRIDE;
const FileDescriptor& aDMDFile) MOZ_OVERRIDE;
virtual bool
DeallocPMemoryReportRequestChild(PMemoryReportRequestChild* actor) MOZ_OVERRIDE;
@ -164,7 +164,7 @@ public:
const uint32_t& aGeneration,
const bool& aAnonymize,
const bool &aMinimizeMemoryUsage,
const nsString &aDMDDumpIdent) MOZ_OVERRIDE;
const FileDescriptor &aDMDFile) MOZ_OVERRIDE;
virtual PCycleCollectWithLogsChild*
AllocPCycleCollectWithLogsChild(const bool& aDumpAllTraces,

View File

@ -103,6 +103,7 @@
#include "nsIURIFixup.h"
#include "nsIWindowWatcher.h"
#include "nsIXULRuntime.h"
#include "nsMemoryInfoDumper.h"
#include "nsMemoryReporterManager.h"
#include "nsServiceManagerUtils.h"
#include "nsStyleSheetService.h"
@ -2457,9 +2458,22 @@ ContentParent::Observe(nsISupports* aSubject,
// The pre-%n part of the string should be all ASCII, so the byte
// offset in identOffset should be correct as a char offset.
MOZ_ASSERT(cmsg[identOffset - 1] == '=');
FileDescriptor dmdFileDesc;
#ifdef MOZ_DMD
FILE *dmdFile;
nsAutoString dmdIdent(Substring(msg, identOffset));
nsresult rv = nsMemoryInfoDumper::OpenDMDFile(dmdIdent, Pid(), &dmdFile);
if (NS_WARN_IF(NS_FAILED(rv))) {
// Proceed with the memory report as if DMD were disabled.
dmdFile = nullptr;
}
if (dmdFile) {
dmdFileDesc = FILEToFileDescriptor(dmdFile);
fclose(dmdFile);
}
#endif
unused << SendPMemoryReportRequestConstructor(
generation, anonymize, minimize,
nsString(Substring(msg, identOffset)));
generation, anonymize, minimize, dmdFileDesc);
}
}
else if (!strcmp(aTopic, "child-gc-request")){
@ -2804,7 +2818,7 @@ PMemoryReportRequestParent*
ContentParent::AllocPMemoryReportRequestParent(const uint32_t& aGeneration,
const bool &aAnonymize,
const bool &aMinimizeMemoryUsage,
const nsString &aDMDDumpIdent)
const FileDescriptor &aDMDFile)
{
MemoryReportRequestParent* parent = new MemoryReportRequestParent();
return parent;

View File

@ -422,7 +422,7 @@ private:
AllocPMemoryReportRequestParent(const uint32_t& aGeneration,
const bool &aAnonymize,
const bool &aMinimizeMemoryUsage,
const nsString &aDMDDumpIdent) MOZ_OVERRIDE;
const FileDescriptor &aDMDFile) MOZ_OVERRIDE;
virtual bool DeallocPMemoryReportRequestParent(PMemoryReportRequestParent* actor) MOZ_OVERRIDE;
virtual PCycleCollectWithLogsParent*

View File

@ -352,7 +352,7 @@ child:
async SetProcessSandbox();
PMemoryReportRequest(uint32_t generation, bool anonymize,
bool minimizeMemoryUsage, nsString DMDDumpIdent);
bool minimizeMemoryUsage, FileDescriptor DMDFile);
/**
* Notify the AudioChannelService in the child processes.

View File

@ -47,9 +47,14 @@ nsGZFileWriter::Init(nsIFile* aFile)
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return InitANSIFileDesc(file);
}
mGZFile = gzdopen(dup(fileno(file)), "wb");
fclose(file);
NS_IMETHODIMP
nsGZFileWriter::InitANSIFileDesc(FILE* aFile)
{
mGZFile = gzdopen(dup(fileno(aFile)), "wb");
fclose(aFile);
// gzdopen returns nullptr on error.
if (NS_WARN_IF(!mGZFile)) {

View File

@ -8,9 +8,11 @@
%{C++
#include "nsDependentString.h"
#include <stdio.h>
%}
interface nsIFile;
[ptr] native FILE(FILE);
/**
* A simple interface for writing to a .gz file.
@ -22,7 +24,7 @@ interface nsIFile;
* The standard gunzip tool cannot decompress a raw gzip stream, but can handle
* the files produced by this interface.
*/
[scriptable, uuid(a256f26a-c603-459e-b5a4-53b4877f2cd8)]
[scriptable, uuid(6bd5642c-1b90-4499-ba4b-199f27efaba5)]
interface nsIGZFileWriter : nsISupports
{
/**
@ -34,6 +36,12 @@ interface nsIGZFileWriter : nsISupports
*/
void init(in nsIFile file);
/**
* Alternate version of init() for use when the file is already opened;
* e.g., with a FileDescriptor passed over IPC.
*/
[noscript] void initANSIFileDesc(in FILE file);
/**
* Write the given string to the file.
*/

View File

@ -5,10 +5,14 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
%{C++
#include <stdio.h>
%}
interface nsIDOMWindow;
interface nsIRunnable;
interface nsISimpleEnumerator;
[ptr] native FILE(FILE);
/*
* Memory reporters measure Firefox's memory usage. They are primarily used to
@ -201,7 +205,7 @@ interface nsIFinishReportingCallback : nsISupports
void callback(in nsISupports data);
};
[scriptable, builtinclass, uuid(c27f8662-a0b7-45b3-8207-14d66b02b9c5)]
[scriptable, builtinclass, uuid(51e17609-e98a-47cc-9f95-095ef3c3823e)]
interface nsIMemoryReporterManager : nsISupports
{
/*
@ -294,15 +298,14 @@ interface nsIMemoryReporterManager : nsISupports
in boolean anonymize);
/*
* As above, but if DMD is enabled and |DMDDumpIdent| is non-empty
* then write a DMD report to a file in the usual temporary directory (see
* |dumpMemoryInfoToTempDir| in |nsIMemoryInfoDumper|.)
* As above, but if DMD is enabled and |DMDFile| is non-null then
* write a DMD report to that file and close it.
*/
[noscript] void
getReportsForThisProcessExtended(in nsIMemoryReporterCallback handleReport,
in nsISupports handleReportData,
in boolean anonymize,
in AString DMDDumpIdent);
in FILE DMDFile);
/*
* The memory reporter manager, for the most part, treats reporters

View File

@ -508,12 +508,12 @@ NS_IMPL_ISUPPORTS(DumpReportCallback, nsIHandleReportCallback)
static void
MakeFilename(const char* aPrefix, const nsAString& aIdentifier,
const char* aSuffix, nsACString& aResult)
int aPid, const char* aSuffix, nsACString& aResult)
{
aResult = nsPrintfCString("%s-%s-%d.%s",
aPrefix,
NS_ConvertUTF16toUTF8(aIdentifier).get(),
getpid(), aSuffix);
aPid, aSuffix);
}
#ifdef MOZ_DMD
@ -633,7 +633,8 @@ nsMemoryInfoDumper::DumpMemoryInfoToTempDir(const nsAString& aIdentifier,
// each process as was the case before bug 946407. This is so that
// the get_about_memory.py script in the B2G repository can
// determine when it's done waiting for files to appear.
MakeFilename("unified-memory-report", identifier, "json.gz", mrFilename);
MakeFilename("unified-memory-report", identifier, getpid(), "json.gz",
mrFilename);
nsCOMPtr<nsIFile> mrTmpFile;
nsresult rv;
@ -676,24 +677,25 @@ nsMemoryInfoDumper::DumpMemoryInfoToTempDir(const nsAString& aIdentifier,
#ifdef MOZ_DMD
nsresult
nsMemoryInfoDumper::DumpDMD(const nsAString& aIdentifier)
nsMemoryInfoDumper::OpenDMDFile(const nsAString& aIdentifier, int aPid,
FILE** aOutFile)
{
if (!dmd::IsRunning()) {
*aOutFile = nullptr;
return NS_OK;
}
nsresult rv;
// Create a filename like dmd-<identifier>-<pid>.txt.gz, which will be used
// if DMD is enabled.
nsCString dmdFilename;
MakeFilename("dmd", aIdentifier, "txt.gz", dmdFilename);
MakeFilename("dmd", aIdentifier, aPid, "txt.gz", dmdFilename);
// Open a new DMD file named |dmdFilename| in NS_OS_TEMP_DIR for writing,
// and dump DMD output to it. This must occur after the memory reporters
// have been run (above), but before the memory-reports file has been
// renamed (so scripts can detect the DMD file, if present).
nsresult rv;
nsCOMPtr<nsIFile> dmdFile;
rv = nsDumpUtils::OpenTempFile(dmdFilename,
getter_AddRefs(dmdFile),
@ -701,15 +703,21 @@ nsMemoryInfoDumper::DumpDMD(const nsAString& aIdentifier)
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = dmdFile->OpenANSIFileDesc("wb", aOutFile);
NS_WARN_IF(NS_FAILED(rv));
return rv;
}
nsresult
nsMemoryInfoDumper::DumpDMDToFile(FILE* aFile)
{
nsRefPtr<nsGZFileWriter> dmdWriter = new nsGZFileWriter();
rv = dmdWriter->Init(dmdFile);
nsresult rv = dmdWriter->InitANSIFileDesc(aFile);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Dump DMD output to the file.
DMDWriteState state(dmdWriter);
dmd::Writer w(DMDWrite, &state);
dmd::Dump(w);
@ -718,6 +726,21 @@ nsMemoryInfoDumper::DumpDMD(const nsAString& aIdentifier)
NS_WARN_IF(NS_FAILED(rv));
return rv;
}
nsresult
nsMemoryInfoDumper::DumpDMD(const nsAString& aIdentifier)
{
nsresult rv;
FILE* dmdFile;
rv = OpenDMDFile(aIdentifier, getpid(), &dmdFile);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!dmdFile) {
return NS_OK;
}
return DumpDMDToFile(dmdFile);
}
#endif // MOZ_DMD
NS_IMETHODIMP

View File

@ -8,6 +8,7 @@
#define mozilla_nsMemoryInfoDumper_h
#include "nsIMemoryInfoDumper.h"
#include <stdio.h>
class nsACString;
@ -31,7 +32,14 @@ public:
static void Initialize();
#ifdef MOZ_DMD
// Write a DMD report.
static nsresult DumpDMD(const nsAString& aIdentifier);
// Open an appropriately named file for a DMD report. If DMD is
// disabled, return a null FILE* instead.
static nsresult OpenDMDFile(const nsAString& aIdentifier, int aPid,
FILE** aOutFile);
// Write a DMD report to the given file and close it.
static nsresult DumpDMDToFile(FILE* aFile);
#endif
};

View File

@ -1104,8 +1104,17 @@ nsMemoryReporterManager::StartGettingReports()
GetReportsState* s = mGetReportsState;
// Get reports for this process.
FILE *parentDMDFile = nullptr;
#ifdef MOZ_DMD
nsresult rv = nsMemoryInfoDumper::OpenDMDFile(s->mDMDDumpIdent, getpid(),
&parentDMDFile);
if (NS_WARN_IF(NS_FAILED(rv))) {
// Proceed with the memory report as if DMD were disabled.
parentDMDFile = nullptr;
}
#endif
GetReportsForThisProcessExtended(s->mHandleReport, s->mHandleReportData,
s->mAnonymize, s->mDMDDumpIdent);
s->mAnonymize, parentDMDFile);
s->mParentDone = true;
// If there are no remaining child processes, we can finish up immediately.
@ -1138,13 +1147,13 @@ nsMemoryReporterManager::GetReportsForThisProcess(
nsISupports* aHandleReportData, bool aAnonymize)
{
return GetReportsForThisProcessExtended(aHandleReport, aHandleReportData,
aAnonymize, nsString());
aAnonymize, nullptr);
}
NS_IMETHODIMP
nsMemoryReporterManager::GetReportsForThisProcessExtended(
nsIHandleReportCallback* aHandleReport, nsISupports* aHandleReportData,
bool aAnonymize, const nsAString& aDMDDumpIdent)
bool aAnonymize, FILE* aDMDFile)
{
// Memory reporters are not necessarily threadsafe, so this function must
// be called from the main thread.
@ -1153,11 +1162,13 @@ nsMemoryReporterManager::GetReportsForThisProcessExtended(
}
#ifdef MOZ_DMD
if (!aDMDDumpIdent.IsEmpty()) {
if (aDMDFile) {
// Clear DMD's reportedness state before running the memory
// reporters, to avoid spurious twice-reported warnings.
dmd::ClearReports();
}
#else
MOZ_ASSERT(!aDMDFile);
#endif
MemoryReporterArray allReporters;
@ -1172,8 +1183,8 @@ nsMemoryReporterManager::GetReportsForThisProcessExtended(
}
#ifdef MOZ_DMD
if (!aDMDDumpIdent.IsEmpty()) {
return nsMemoryInfoDumper::DumpDMD(aDMDDumpIdent);
if (aDMDFile) {
return nsMemoryInfoDumper::DumpDMDToFile(aDMDFile);
}
#endif