mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-25 20:01:50 +00:00
Bug 853358 - Add plugin profiling support. r=ehsan,bsmedberg
--HG-- extra : rebase_source : 70dfd31809cb644f6405bd144a2e8373268a461c
This commit is contained in:
parent
d22f45504a
commit
4a868d8c33
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -160,6 +160,9 @@ protected:
|
||||
virtual bool
|
||||
RecvProcessNativeEventsInRPCCall() MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
AnswerGeckoGetProfile(nsCString* aProfile);
|
||||
|
||||
public:
|
||||
PluginModuleChild();
|
||||
virtual ~PluginModuleChild();
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -42,6 +42,7 @@ CPPSRCS = \
|
||||
nsProfilerFactory.cpp \
|
||||
nsProfiler.cpp \
|
||||
TableTicker.cpp \
|
||||
SaveProfileTask.cpp \
|
||||
BreakpadSampler.cpp \
|
||||
UnwinderThread2.cpp \
|
||||
ProfileEntry.cpp \
|
||||
|
@ -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
|
||||
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();
|
||||
|
94
tools/profiler/SaveProfileTask.cpp
Normal file
94
tools/profiler/SaveProfileTask.cpp
Normal 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;
|
||||
}
|
||||
|
@ -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());
|
||||
|
||||
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
|
||||
NS_IMETHOD Run();
|
||||
};
|
||||
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);
|
||||
class ProfileSaveEvent MOZ_FINAL : public nsIProfileSaveEvent {
|
||||
public:
|
||||
typedef void (*AddSubProfileFunc)(const char* aProfile, void* aClosure);
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
ProfileSaveEvent(AddSubProfileFunc aFunc, void* aClosure)
|
||||
: mFunc(aFunc)
|
||||
, mClosure(aClosure)
|
||||
{}
|
||||
|
||||
~ProfileSaveEvent() {}
|
||||
|
||||
NS_IMETHOD AddSubProfile(const char* aProfile);
|
||||
private:
|
||||
AddSubProfileFunc mFunc;
|
||||
void* mClosure;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -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
|
||||
|
@ -7,6 +7,7 @@
|
||||
if CONFIG['MOZ_ENABLE_PROFILER_SPS']:
|
||||
MODULE = 'profiler'
|
||||
XPIDL_SOURCES += [
|
||||
'nsIProfileSaveEvent.idl',
|
||||
'nsIProfiler.idl',
|
||||
]
|
||||
EXPORTS += [
|
||||
|
19
tools/profiler/nsIProfileSaveEvent.idl
Normal file
19
tools/profiler/nsIProfileSaveEvent.idl
Normal 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);
|
||||
};
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user