Bug 853358 - Add plugin profiling support. r=ehsan,bsmedberg

--HG--
extra : rebase_source : 70dfd31809cb644f6405bd144a2e8373268a461c
This commit is contained in:
Benoit Girard 2013-03-21 10:17:23 +01:00
parent d22f45504a
commit 4a868d8c33
14 changed files with 260 additions and 84 deletions

View File

@ -84,6 +84,9 @@ child:
rpc PCrashReporter()
returns (NativeThreadId tid, uint32_t processType);
rpc GeckoGetProfile()
returns (nsCString aProfile);
parent:
/**
* This message is only used on X11 platforms.

View File

@ -56,6 +56,8 @@
#include "PluginUtilsOSX.h"
#endif
#include "GeckoProfiler.h"
using namespace mozilla;
using namespace mozilla::plugins;
using mozilla::dom::CrashReporterChild;
@ -2425,3 +2427,16 @@ PluginModuleChild::ProcessNativeEvents() {
CallProcessSomeEvents();
}
#endif
bool
PluginModuleChild::AnswerGeckoGetProfile(nsCString* aProfile) {
char* profile = profiler_get_profile();
if (profile != NULL) {
*aProfile = nsCString(profile, strlen(profile));
free(profile);
} else {
*aProfile = nsCString("", 0);
}
return true;
}

View File

@ -160,6 +160,9 @@ protected:
virtual bool
RecvProcessNativeEventsInRPCCall() MOZ_OVERRIDE;
virtual bool
AnswerGeckoGetProfile(nsCString* aProfile);
public:
PluginModuleChild();
virtual ~PluginModuleChild();

View File

@ -38,7 +38,9 @@
#include "PluginHangUIParent.h"
#include "mozilla/widget/AudioSession.h"
#endif
#include "GeckoProfiler.h"
#include "nsIProfileSaveEvent.h"
#include "mozilla/Services.h"
#include "nsIObserverService.h"
using base::KillProcess;
@ -138,12 +140,16 @@ PluginModuleParent::PluginModuleParent(const char* aFilePath)
Preferences::RegisterCallback(TimeoutChanged, kHangUITimeoutPref, this);
Preferences::RegisterCallback(TimeoutChanged, kHangUIMinDisplayPref, this);
#endif
InitPluginProfiling();
}
PluginModuleParent::~PluginModuleParent()
{
NS_ASSERTION(OkToCleanup(), "unsafe destruction");
ShutdownPluginProfiling();
if (!mShutdown) {
NS_WARNING("Plugin host deleted the module without shutting down.");
NPError err;
@ -1710,3 +1716,56 @@ PluginModuleParent::OnCrash(DWORD processID)
}
#endif // MOZ_CRASHREPORTER_INJECTOR
class PluginProfilerObserver MOZ_FINAL : public nsIObserver,
public nsSupportsWeakReference
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
explicit PluginProfilerObserver(PluginModuleParent* pmp)
: mPmp(pmp)
{}
private:
PluginModuleParent* mPmp;
};
NS_IMPL_ISUPPORTS2(PluginProfilerObserver, nsIObserver, nsISupportsWeakReference)
NS_IMETHODIMP
PluginProfilerObserver::Observe(nsISupports *aSubject,
const char *aTopic,
const PRUnichar *aData)
{
nsCOMPtr<nsIProfileSaveEvent> pse = do_QueryInterface(aSubject);
if (pse) {
nsCString result;
bool success = mPmp->CallGeckoGetProfile(&result);
if (success && !result.IsEmpty()) {
pse->AddSubProfile(result.get());
}
}
return NS_OK;
}
void
PluginModuleParent::InitPluginProfiling()
{
nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
if (observerService) {
mProfilerObserver = new PluginProfilerObserver(this);
observerService->AddObserver(mProfilerObserver, "profiler-subprocess", false);
}
}
void
PluginModuleParent::ShutdownPluginProfiling()
{
nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
if (observerService) {
observerService->RemoveObserver(mProfilerObserver, "profiler-subprocess");
}
}

View File

@ -301,6 +301,9 @@ private:
static int TimeoutChanged(const char* aPref, void* aModule);
void NotifyPluginCrashed();
void InitPluginProfiling();
void ShutdownPluginProfiling();
PluginProcessParent* mSubprocess;
// the plugin thread in mSubprocess
NativeThreadId mPluginThread;
@ -314,6 +317,7 @@ private:
nsString mPluginDumpID;
nsString mBrowserDumpID;
nsString mHangID;
nsRefPtr<nsIObserver> mProfilerObserver;
#ifdef XP_WIN
InfallibleTArray<float> mPluginCpuUsageOnHang;
PluginHangUIParent *mHangUIParent;

View File

@ -19,7 +19,7 @@
#include "jsfriendapi.h"
#include "GeckoProfilerFunc.h"
#include "PseudoStack.h"
#include "nsISupports.h"
/* QT has a #define for the word "slots" and jsfriendapi.h has a struct with
* this variable name, causing compilation problems. Alleviate this for now by

View File

@ -42,6 +42,7 @@ CPPSRCS = \
nsProfilerFactory.cpp \
nsProfiler.cpp \
TableTicker.cpp \
SaveProfileTask.cpp \
BreakpadSampler.cpp \
UnwinderThread2.cpp \
ProfileEntry.cpp \

View File

@ -7,6 +7,7 @@
#include "GeckoProfilerImpl.h"
#include "platform.h"
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
// JSON
#include "JSObjectBuilder.h"
@ -307,8 +308,15 @@ JSCustomObject* ThreadProfile::ToJSObject(JSContext *aCx)
}
void ThreadProfile::BuildJSObject(JSAObjectBuilder& b, JSCustomObject* profile) {
// Thread meta data
b.DefineProperty(profile, "name", mName);
if (XRE_GetProcessType() == GeckoProcessType_Plugin) {
// TODO Add the proper plugin name
b.DefineProperty(profile, "name", "Plugin");
} else {
b.DefineProperty(profile, "name", mName);
}
b.DefineProperty(profile, "tid", mThreadId);
JSCustomArray *samples = b.CreateArray();

View File

@ -0,0 +1,94 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "SaveProfileTask.h"
#include "GeckoProfilerImpl.h"
static JSBool
WriteCallback(const jschar *buf, uint32_t len, void *data)
{
std::ofstream& stream = *static_cast<std::ofstream*>(data);
nsAutoCString profile = NS_ConvertUTF16toUTF8(buf, len);
stream << profile.Data();
return JS_TRUE;
}
nsresult
SaveProfileTask::Run() {
// Get file path
#if defined(SPS_PLAT_arm_android) && !defined(MOZ_WIDGET_GONK)
nsCString tmpPath;
tmpPath.AppendPrintf("/sdcard/profile_%i_%i.txt", XRE_GetProcessType(), getpid());
#else
nsCOMPtr<nsIFile> tmpFile;
nsAutoCString tmpPath;
if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpFile)))) {
LOG("Failed to find temporary directory.");
return NS_ERROR_FAILURE;
}
tmpPath.AppendPrintf("profile_%i_%i.txt", XRE_GetProcessType(), getpid());
nsresult rv = tmpFile->AppendNative(tmpPath);
if (NS_FAILED(rv))
return rv;
rv = tmpFile->GetNativePath(tmpPath);
if (NS_FAILED(rv))
return rv;
#endif
// Create a JSContext to run a JSObjectBuilder :(
// Based on XPCShellEnvironment
JSRuntime *rt;
JSContext *cx;
nsCOMPtr<nsIJSRuntimeService> rtsvc
= do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
if (!rtsvc || NS_FAILED(rtsvc->GetRuntime(&rt)) || !rt) {
LOG("failed to get RuntimeService");
return NS_ERROR_FAILURE;;
}
cx = JS_NewContext(rt, 8192);
if (!cx) {
LOG("Failed to get context");
return NS_ERROR_FAILURE;
}
{
JSAutoRequest ar(cx);
static JSClass c = {
"global", JSCLASS_GLOBAL_FLAGS,
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
};
JSObject *obj = JS_NewGlobalObject(cx, &c, NULL);
std::ofstream stream;
stream.open(tmpPath.get());
if (stream.is_open()) {
JSAutoCompartment autoComp(cx, obj);
JSObject* profileObj = profiler_get_profile_jsobject(cx);
jsval val = OBJECT_TO_JSVAL(profileObj);
JS_Stringify(cx, &val, nullptr, JSVAL_NULL, WriteCallback, &stream);
stream.close();
LOGF("Saved to %s", tmpPath.get());
} else {
LOG("Fail to open profile log file.");
}
}
JS_EndRequest(cx);
JS_DestroyContext(cx);
return NS_OK;
}
NS_IMPL_ISUPPORTS1(ProfileSaveEvent, nsIProfileSaveEvent)
nsresult
ProfileSaveEvent::AddSubProfile(const char* aProfile) {
mFunc(aProfile, mClosure);
return NS_OK;
}

View File

@ -12,7 +12,12 @@
#include "nsDirectoryServiceUtils.h"
#include "nsDirectoryServiceDefs.h"
#include "nsXULAppAPI.h"
#include "jsfriendapi.h"
#include "nsIJSRuntimeService.h"
#include "nsIProfileSaveEvent.h"
#include <iostream>
#include <fstream>
#ifdef XP_WIN
#include <windows.h>
@ -21,15 +26,6 @@
#include <unistd.h>
#endif
static JSBool
WriteCallback(const jschar *buf, uint32_t len, void *data)
{
std::ofstream& stream = *static_cast<std::ofstream*>(data);
nsAutoCString profile = NS_ConvertUTF16toUTF8(buf, len);
stream << profile.Data();
return JS_TRUE;
}
/**
* This is an event used to save the profile on the main thread
* to be sure that it is not being modified while saving.
@ -38,73 +34,25 @@ class SaveProfileTask : public nsRunnable {
public:
SaveProfileTask() {}
NS_IMETHOD Run() {
// Get file path
# if defined(SPS_PLAT_arm_android) && !defined(MOZ_WIDGET_GONK)
nsCString tmpPath;
tmpPath.AppendPrintf("/sdcard/profile_%i_%i.txt", XRE_GetProcessType(), getpid());
# else
nsCOMPtr<nsIFile> tmpFile;
nsAutoCString tmpPath;
if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpFile)))) {
LOG("Failed to find temporary directory.");
return NS_ERROR_FAILURE;
}
tmpPath.AppendPrintf("profile_%i_%i.txt", XRE_GetProcessType(), getpid());
NS_IMETHOD Run();
};
nsresult rv = tmpFile->AppendNative(tmpPath);
if (NS_FAILED(rv))
return rv;
class ProfileSaveEvent MOZ_FINAL : public nsIProfileSaveEvent {
public:
typedef void (*AddSubProfileFunc)(const char* aProfile, void* aClosure);
NS_DECL_ISUPPORTS
rv = tmpFile->GetNativePath(tmpPath);
if (NS_FAILED(rv))
return rv;
# endif
ProfileSaveEvent(AddSubProfileFunc aFunc, void* aClosure)
: mFunc(aFunc)
, mClosure(aClosure)
{}
// Create a JSContext to run a JSObjectBuilder :(
// Based on XPCShellEnvironment
JSRuntime *rt;
JSContext *cx;
nsCOMPtr<nsIJSRuntimeService> rtsvc
= do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
if (!rtsvc || NS_FAILED(rtsvc->GetRuntime(&rt)) || !rt) {
LOG("failed to get RuntimeService");
return NS_ERROR_FAILURE;;
}
~ProfileSaveEvent() {}
cx = JS_NewContext(rt, 8192);
if (!cx) {
LOG("Failed to get context");
return NS_ERROR_FAILURE;
}
{
JSAutoRequest ar(cx);
static JSClass c = {
"global", JSCLASS_GLOBAL_FLAGS,
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
};
JSObject *obj = JS_NewGlobalObject(cx, &c, NULL);
std::ofstream stream;
stream.open(tmpPath.get());
if (stream.is_open()) {
JSAutoCompartment autoComp(cx, obj);
JSObject* profileObj = profiler_get_profile_jsobject(cx);
jsval val = OBJECT_TO_JSVAL(profileObj);
JS_Stringify(cx, &val, nullptr, JSVAL_NULL, WriteCallback, &stream);
stream.close();
LOGF("Saved to %s", tmpPath.get());
} else {
LOGF("Fail to open profile log file '%s'.", tmpPath.get());
}
}
JS_EndRequest(cx);
JS_DestroyContext(cx);
return NS_OK;
}
NS_IMETHOD AddSubProfile(const char* aProfile);
private:
AddSubProfileFunc mFunc;
void* mClosure;
};
#endif

View File

@ -16,6 +16,7 @@
#include "shared-libraries.h"
#include "mozilla/StackWalk.h"
#include "TableTicker.h"
#include "nsXULAppAPI.h"
// JSON
#include "JSObjectBuilder.h"
@ -164,6 +165,20 @@ JSObject* TableTicker::ToJSObject(JSContext *aCx)
return jsProfile;
}
struct SubprocessClosure {
JSAObjectBuilder* mBuilder;
JSCustomArray* mThreads;
};
void SubProcessCallback(const char* aProfile, void* aClosure)
{
// Called by the observer to get their profile data included
// as a sub profile
SubprocessClosure* closure = (SubprocessClosure*)aClosure;
closure->mBuilder->ArrayPush(closure->mThreads, aProfile);
}
#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
static
JSCustomObject* BuildJavaThreadJSObject(JSAObjectBuilder& b)
@ -255,6 +270,17 @@ void TableTicker::BuildJSObject(JSAObjectBuilder& b, JSCustomObject* profile)
#endif
SetPaused(false);
// Send a event asking any subprocesses (plugins) to
// give us their information
SubprocessClosure closure;
closure.mBuilder = &b;
closure.mThreads = threads;
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
if (os) {
nsRefPtr<ProfileSaveEvent> pse = new ProfileSaveEvent(SubProcessCallback, &closure);
os->NotifyObservers(pse, "profiler-subprocess", nullptr);
}
}
// END SaveProfileTask et al

View File

@ -7,6 +7,7 @@
if CONFIG['MOZ_ENABLE_PROFILER_SPS']:
MODULE = 'profiler'
XPIDL_SOURCES += [
'nsIProfileSaveEvent.idl',
'nsIProfiler.idl',
]
EXPORTS += [

View File

@ -0,0 +1,19 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "nsISupports.idl"
[uuid(f5ad0830-e178-41f9-b253-db9b4fae4cb3)]
interface nsIProfileSaveEvent : nsISupports
{
/**
* Call this method when observing this event to include
* a sub profile origining from an external source such
* as a non native thread or another process.
*/
void AddSubProfile(in string aMarker);
};

View File

@ -350,15 +350,10 @@ char* mozilla_sampler_get_profile()
return NULL;
}
std::stringstream profile;
t->SetPaused(true);
profile << *(t->GetPrimaryThreadProfile());
t->SetPaused(false);
std::string profileString = profile.str();
char *rtn = (char*)malloc( (profileString.length() + 1) * sizeof(char) );
strcpy(rtn, profileString.c_str());
return rtn;
std::stringstream stream;
t->ToStreamAsJSON(stream);
char* profile = strdup(stream.str().c_str());
return profile;
}
JSObject *mozilla_sampler_get_profile_data(JSContext *aCx)