Remove SPS profiler.

- Conditionals and code blocks. (MOZ_ENABLE_PROFILER_SPS)
- Stub out several profiler-only functions.
This commit is contained in:
wolfbeast 2018-05-24 14:06:04 +02:00 committed by Roy Tam
commit 1a823f5ad6
113 changed files with 12 additions and 24501 deletions

View file

@ -169,14 +169,10 @@
#endif
#ifdef NS_PRINTING
#endif
#ifdef MOZ_ENABLE_PROFILER_SPS
#endif
#ifdef NECKO_WIFI
#endif
#ifdef MOZ_WEBRTC
#endif
#ifdef MOZ_ENABLE_PROFILER_SPS
#endif
#ifdef MOZ_ENABLE_XREMOTE
#endif
#ifdef XP_MACOSX

View file

@ -251,9 +251,6 @@
@RESPATH@/components/layout_xul.xpt
@RESPATH@/components/locale.xpt
@RESPATH@/components/lwbrk.xpt
#ifdef MOZ_ENABLE_PROFILER_SPS
@RESPATH@/components/memory_profiler.xpt
#endif
@RESPATH@/browser/components/migration.xpt
@RESPATH@/components/mimetype.xpt
@RESPATH@/components/mozfind.xpt
@ -285,9 +282,6 @@
@RESPATH@/components/plugin.xpt
@RESPATH@/components/pref.xpt
@RESPATH@/components/prefetch.xpt
#ifdef MOZ_ENABLE_PROFILER_SPS
@RESPATH@/components/profiler.xpt
#endif
@RESPATH@/components/rdf.xpt
@RESPATH@/components/satchel.xpt
@RESPATH@/components/saxparser.xpt

View file

@ -13470,10 +13470,6 @@ nsresult
ConnectionPool::
ThreadRunnable::Run()
{
#ifdef MOZ_ENABLE_PROFILER_SPS
char stackTopGuess;
#endif // MOZ_ENABLE_PROFILER_SPS
MOZ_ASSERT(!IsOnBackgroundThread());
MOZ_ASSERT(mContinueRunning);
@ -13490,10 +13486,6 @@ ThreadRunnable::Run()
const nsPrintfCString threadName("IndexedDB #%lu", mSerialNumber);
PR_SetCurrentThreadName(threadName.get());
#ifdef MOZ_ENABLE_PROFILER_SPS
profiler_register_thread(threadName.get(), &stackTopGuess);
#endif // MOZ_ENABLE_PROFILER_SPS
}
{
@ -13537,10 +13529,6 @@ ThreadRunnable::Run()
}
}
#ifdef MOZ_ENABLE_PROFILER_SPS
profiler_unregister_thread();
#endif // MOZ_ENABLE_PROFILER_SPS
return NS_OK;
}

View file

@ -153,7 +153,7 @@ const char kPrefMaxSerilizedMsgSize[] = IDB_PREF_BRANCH_ROOT "maxSerializedMsgSi
const char kPrefLoggingEnabled[] = IDB_PREF_LOGGING_BRANCH_ROOT "enabled";
const char kPrefLoggingDetails[] = IDB_PREF_LOGGING_BRANCH_ROOT "details";
#if defined(DEBUG) || defined(MOZ_ENABLE_PROFILER_SPS)
#if defined(DEBUG)
const char kPrefLoggingProfiler[] =
IDB_PREF_LOGGING_BRANCH_ROOT "profiler-marks";
#endif
@ -415,10 +415,7 @@ IndexedDatabaseManager::Init()
Preferences::RegisterCallback(LoggingModePrefChangedCallback,
kPrefLoggingDetails);
#ifdef MOZ_ENABLE_PROFILER_SPS
Preferences::RegisterCallback(LoggingModePrefChangedCallback,
kPrefLoggingProfiler);
#endif
Preferences::RegisterCallbackAndCall(LoggingModePrefChangedCallback,
kPrefLoggingEnabled);
@ -479,10 +476,6 @@ IndexedDatabaseManager::Destroy()
Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
kPrefLoggingDetails);
#ifdef MOZ_ENABLE_PROFILER_SPS
Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
kPrefLoggingProfiler);
#endif
Preferences::UnregisterCallback(LoggingModePrefChangedCallback,
kPrefLoggingEnabled);
@ -1060,15 +1053,13 @@ IndexedDatabaseManager::LoggingModePrefChangedCallback(
}
bool useProfiler =
#if defined(DEBUG) || defined(MOZ_ENABLE_PROFILER_SPS)
#if defined(DEBUG)
Preferences::GetBool(kPrefLoggingProfiler);
#if !defined(MOZ_ENABLE_PROFILER_SPS)
if (useProfiler) {
NS_WARNING("IndexedDB cannot create profiler marks because this build does "
"not have profiler extensions enabled!");
useProfiler = false;
}
#endif
#else
false;
#endif

View file

@ -2612,15 +2612,6 @@ ContentChild::RecvShutdown()
GetIPCChannel()->SetAbortOnError(false);
#ifdef MOZ_ENABLE_PROFILER_SPS
if (profiler_is_active()) {
// We're shutting down while we were profiling. Send the
// profile up to the parent so that we don't lose this
// information.
Unused << RecvGatherProfile();
}
#endif
// Start a timer that will insure we quickly exit after a reasonable
// period of time. Prevents shutdown hangs after our connection to the
// parent closes.

View file

@ -81,9 +81,6 @@
#include "mozilla/Preferences.h"
#include "mozilla/ProcessHangMonitor.h"
#include "mozilla/ProcessHangMonitorIPC.h"
#ifdef MOZ_ENABLE_PROFILER_SPS
#include "mozilla/ProfileGatherer.h"
#endif
#include "mozilla/ScopeExit.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
@ -220,11 +217,6 @@
#include "nsIBrowserSearchService.h"
#endif
#ifdef MOZ_ENABLE_PROFILER_SPS
#include "nsIProfiler.h"
#include "nsIProfileSaveEvent.h"
#endif
#ifdef XP_WIN
#include "mozilla/widget/AudioSession.h"
#endif
@ -245,9 +237,6 @@ extern const char* kForceEnableE10sPref;
using base::ChildPrivileges;
using base::KillProcess;
#ifdef MOZ_ENABLE_PROFILER_SPS
using mozilla::ProfileGatherer;
#endif
using namespace mozilla::dom::power;
using namespace mozilla::media;
@ -514,14 +503,6 @@ static const char* sObserverTopics[] = {
"file-watcher-update",
#ifdef ACCESSIBILITY
"a11y-init-or-shutdown",
#endif
#ifdef MOZ_ENABLE_PROFILER_SPS
"profiler-started",
"profiler-stopped",
"profiler-paused",
"profiler-resumed",
"profiler-subprocess-gather",
"profiler-subprocess",
#endif
"cacheservice:empty-cache",
};
@ -1305,26 +1286,6 @@ ContentParent::Init()
}
#endif
#ifdef MOZ_ENABLE_PROFILER_SPS
nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1"));
bool profilerActive = false;
DebugOnly<nsresult> rv = profiler->IsActive(&profilerActive);
MOZ_ASSERT(NS_SUCCEEDED(rv));
if (profilerActive) {
nsCOMPtr<nsIProfilerStartParams> currentProfilerParams;
rv = profiler->GetStartParams(getter_AddRefs(currentProfilerParams));
MOZ_ASSERT(NS_SUCCEEDED(rv));
nsCOMPtr<nsISupports> gatherer;
rv = profiler->GetProfileGatherer(getter_AddRefs(gatherer));
MOZ_ASSERT(NS_SUCCEEDED(rv));
mGatherer = static_cast<ProfileGatherer*>(gatherer.get());
StartProfiler(currentProfilerParams);
}
#endif
RefPtr<GeckoMediaPluginServiceParent> gmps(GeckoMediaPluginServiceParent::GetSingleton());
gmps->UpdateContentProcessGMPCapabilities();
}
@ -1751,12 +1712,6 @@ ContentParent::ActorDestroy(ActorDestroyReason why)
mConsoleService = nullptr;
#ifdef MOZ_ENABLE_PROFILER_SPS
if (mGatherer && !mProfile.IsEmpty()) {
mGatherer->OOPExitProfile(mProfile);
}
#endif
if (obs) {
RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
@ -2556,27 +2511,6 @@ ContentParent::Observe(nsISupports* aSubject,
NS_ASSERTION(!mSubprocess, "Close should have nulled mSubprocess");
}
#ifdef MOZ_ENABLE_PROFILER_SPS
// Need to do this before the mIsAlive check to avoid missing profiles.
if (!strcmp(aTopic, "profiler-subprocess-gather")) {
if (mGatherer) {
mGatherer->WillGatherOOPProfile();
if (mIsAlive && mSubprocess) {
Unused << SendGatherProfile();
}
}
}
else if (!strcmp(aTopic, "profiler-subprocess")) {
nsCOMPtr<nsIProfileSaveEvent> pse = do_QueryInterface(aSubject);
if (pse) {
if (!mProfile.IsEmpty()) {
pse->AddSubProfile(mProfile.get());
mProfile.Truncate();
}
}
}
#endif
if (!mIsAlive || !mSubprocess)
return NS_OK;
@ -2662,22 +2596,6 @@ ContentParent::Observe(nsISupports* aSubject,
Unused << SendShutdownA11y();
}
}
#endif
#ifdef MOZ_ENABLE_PROFILER_SPS
else if (!strcmp(aTopic, "profiler-started")) {
nsCOMPtr<nsIProfilerStartParams> params(do_QueryInterface(aSubject));
StartProfiler(params);
}
else if (!strcmp(aTopic, "profiler-stopped")) {
mGatherer = nullptr;
Unused << SendStopProfiler();
}
else if (!strcmp(aTopic, "profiler-paused")) {
Unused << SendPauseProfiler(true);
}
else if (!strcmp(aTopic, "profiler-resumed")) {
Unused << SendPauseProfiler(false);
}
#endif
else if (!strcmp(aTopic, "cacheservice:empty-cache")) {
Unused << SendNotifyEmptyHTTPCache();
@ -4559,13 +4477,7 @@ ContentParent::RecvCreateWindow(PBrowserParent* aThisTab,
bool
ContentParent::RecvProfile(const nsCString& aProfile)
{
#ifdef MOZ_ENABLE_PROFILER_SPS
if (NS_WARN_IF(!mGatherer)) {
return true;
}
mProfile = aProfile;
mGatherer->GatheredOOPProfile();
#endif
/*** STUB ***/
return true;
}
@ -4658,29 +4570,7 @@ ContentParent::RecvNotifyBenchmarkResult(const nsString& aCodecName,
void
ContentParent::StartProfiler(nsIProfilerStartParams* aParams)
{
#ifdef MOZ_ENABLE_PROFILER_SPS
if (NS_WARN_IF(!aParams)) {
return;
}
ProfilerInitParams ipcParams;
ipcParams.enabled() = true;
aParams->GetEntries(&ipcParams.entries());
aParams->GetInterval(&ipcParams.interval());
ipcParams.features() = aParams->GetFeatures();
ipcParams.threadFilters() = aParams->GetThreadFilterNames();
Unused << SendStartProfiler(ipcParams);
nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1"));
if (NS_WARN_IF(!profiler)) {
return;
}
nsCOMPtr<nsISupports> gatherer;
profiler->GetProfileGatherer(getter_AddRefs(gatherer));
mGatherer = static_cast<ProfileGatherer*>(gatherer.get());
#endif
/*** STUB ***/
}
bool

View file

@ -42,9 +42,6 @@ class nsIWidget;
namespace mozilla {
class PRemoteSpellcheckEngineParent;
#ifdef MOZ_ENABLE_PROFILER_SPS
class ProfileGatherer;
#endif
namespace embedding {
class PrintingParent;
@ -1128,9 +1125,6 @@ private:
PProcessHangMonitorParent* mHangMonitorActor;
#ifdef MOZ_ENABLE_PROFILER_SPS
RefPtr<mozilla::ProfileGatherer> mGatherer;
#endif
nsCString mProfile;
UniquePtr<gfx::DriverCrashGuard> mDriverCrashGuard;

View file

@ -18,9 +18,6 @@
#include "mozilla/plugins/PluginBridge.h"
#include "mozilla/plugins/PluginInstanceParent.h"
#include "mozilla/Preferences.h"
#ifdef MOZ_ENABLE_PROFILER_SPS
#include "mozilla/ProfileGatherer.h"
#endif
#include "mozilla/ProcessHangMonitor.h"
#include "mozilla/Services.h"
#include "mozilla/Telemetry.h"
@ -46,11 +43,6 @@
#include "PluginUtilsWin.h"
#endif
#ifdef MOZ_ENABLE_PROFILER_SPS
#include "nsIProfiler.h"
#include "nsIProfileSaveEvent.h"
#endif
#ifdef MOZ_WIDGET_GTK
#include <glib.h>
#elif XP_MACOSX
@ -61,9 +53,6 @@
using base::KillProcess;
using mozilla::PluginLibrary;
#ifdef MOZ_ENABLE_PROFILER_SPS
using mozilla::ProfileGatherer;
#endif
using mozilla::ipc::MessageChannel;
using mozilla::ipc::GeckoChildProcessHost;
using mozilla::dom::CrashReporterParent;
@ -544,25 +533,6 @@ PluginModuleChromeParent::OnProcessLaunched(const bool aSucceeded)
}
#endif
}
#ifdef MOZ_ENABLE_PROFILER_SPS
nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1"));
bool profilerActive = false;
DebugOnly<nsresult> rv = profiler->IsActive(&profilerActive);
MOZ_ASSERT(NS_SUCCEEDED(rv));
if (profilerActive) {
nsCOMPtr<nsIProfilerStartParams> currentProfilerParams;
rv = profiler->GetStartParams(getter_AddRefs(currentProfilerParams));
MOZ_ASSERT(NS_SUCCEEDED(rv));
nsCOMPtr<nsISupports> gatherer;
rv = profiler->GetProfileGatherer(getter_AddRefs(gatherer));
MOZ_ASSERT(NS_SUCCEEDED(rv));
mGatherer = static_cast<ProfileGatherer*>(gatherer.get());
StartProfiler(currentProfilerParams);
}
#endif
}
bool
@ -648,10 +618,6 @@ PluginModuleChromeParent::PluginModuleChromeParent(const char* aFilePath,
mSandboxLevel = aSandboxLevel;
mRunID = GeckoChildProcessHost::GetUniqueID();
#ifdef MOZ_ENABLE_PROFILER_SPS
InitPluginProfiling();
#endif
mozilla::HangMonitor::RegisterAnnotator(*this);
}
@ -661,10 +627,6 @@ PluginModuleChromeParent::~PluginModuleChromeParent()
NS_RUNTIMEABORT("unsafe destruction");
}
#ifdef MOZ_ENABLE_PROFILER_SPS
ShutdownPluginProfiling();
#endif
#ifdef XP_WIN
// If we registered for audio notifications, stop.
mozilla::plugins::PluginUtilsWin::RegisterForAudioDeviceChanges(this,
@ -2680,133 +2642,10 @@ PluginModuleParent::AnswerNPN_SetValue_NPPVpluginRequiresAudioDeviceChanges(
return true;
}
#ifdef MOZ_ENABLE_PROFILER_SPS
class PluginProfilerObserver final : public nsIObserver,
public nsSupportsWeakReference
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
explicit PluginProfilerObserver(PluginModuleChromeParent* pmp)
: mPmp(pmp)
{}
private:
~PluginProfilerObserver() {}
PluginModuleChromeParent* mPmp;
};
NS_IMPL_ISUPPORTS(PluginProfilerObserver, nsIObserver, nsISupportsWeakReference)
NS_IMETHODIMP
PluginProfilerObserver::Observe(nsISupports *aSubject,
const char *aTopic,
const char16_t *aData)
{
if (!strcmp(aTopic, "profiler-started")) {
nsCOMPtr<nsIProfilerStartParams> params(do_QueryInterface(aSubject));
mPmp->StartProfiler(params);
} else if (!strcmp(aTopic, "profiler-stopped")) {
mPmp->StopProfiler();
} else if (!strcmp(aTopic, "profiler-subprocess-gather")) {
mPmp->GatherAsyncProfile();
} else if (!strcmp(aTopic, "profiler-subprocess")) {
nsCOMPtr<nsIProfileSaveEvent> pse = do_QueryInterface(aSubject);
mPmp->GatheredAsyncProfile(pse);
}
return NS_OK;
}
void
PluginModuleChromeParent::InitPluginProfiling()
{
nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
if (observerService) {
mProfilerObserver = new PluginProfilerObserver(this);
observerService->AddObserver(mProfilerObserver, "profiler-started", false);
observerService->AddObserver(mProfilerObserver, "profiler-stopped", false);
observerService->AddObserver(mProfilerObserver, "profiler-subprocess-gather", false);
observerService->AddObserver(mProfilerObserver, "profiler-subprocess", false);
}
}
void
PluginModuleChromeParent::ShutdownPluginProfiling()
{
nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
if (observerService) {
observerService->RemoveObserver(mProfilerObserver, "profiler-started");
observerService->RemoveObserver(mProfilerObserver, "profiler-stopped");
observerService->RemoveObserver(mProfilerObserver, "profiler-subprocess-gather");
observerService->RemoveObserver(mProfilerObserver, "profiler-subprocess");
}
}
void
PluginModuleChromeParent::StartProfiler(nsIProfilerStartParams* aParams)
{
if (NS_WARN_IF(!aParams)) {
return;
}
ProfilerInitParams ipcParams;
ipcParams.enabled() = true;
aParams->GetEntries(&ipcParams.entries());
aParams->GetInterval(&ipcParams.interval());
ipcParams.features() = aParams->GetFeatures();
ipcParams.threadFilters() = aParams->GetThreadFilterNames();
Unused << SendStartProfiler(ipcParams);
nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1"));
if (NS_WARN_IF(!profiler)) {
return;
}
nsCOMPtr<nsISupports> gatherer;
profiler->GetProfileGatherer(getter_AddRefs(gatherer));
mGatherer = static_cast<ProfileGatherer*>(gatherer.get());
}
void
PluginModuleChromeParent::StopProfiler()
{
mGatherer = nullptr;
Unused << SendStopProfiler();
}
void
PluginModuleChromeParent::GatherAsyncProfile()
{
if (NS_WARN_IF(!mGatherer)) {
return;
}
mGatherer->WillGatherOOPProfile();
Unused << SendGatherProfile();
}
void
PluginModuleChromeParent::GatheredAsyncProfile(nsIProfileSaveEvent* aSaveEvent)
{
if (aSaveEvent && !mProfile.IsEmpty()) {
aSaveEvent->AddSubProfile(mProfile.get());
mProfile.Truncate();
}
}
#endif // MOZ_ENABLE_PROFILER_SPS
bool
PluginModuleChromeParent::RecvProfile(const nsCString& aProfile)
{
#ifdef MOZ_ENABLE_PROFILER_SPS
if (NS_WARN_IF(!mGatherer)) {
return true;
}
mProfile = aProfile;
mGatherer->GatheredOOPProfile();
#endif
/*** STUB ***/
return true;
}

View file

@ -30,10 +30,6 @@ class nsIProfileSaveEvent;
class nsPluginTag;
namespace mozilla {
#ifdef MOZ_ENABLE_PROFILER_SPS
class ProfileGatherer;
#endif
namespace layers {
class TextureClientRecycleAllocator;
} // namespace layers
@ -467,13 +463,6 @@ class PluginModuleChromeParent
void CachedSettingChanged();
#ifdef MOZ_ENABLE_PROFILER_SPS
void GatherAsyncProfile();
void GatheredAsyncProfile(nsIProfileSaveEvent* aSaveEvent);
void StartProfiler(nsIProfilerStartParams* aParams);
void StopProfiler();
#endif
virtual bool
RecvProfile(const nsCString& aProfile) override;
@ -519,11 +508,6 @@ private:
virtual void UpdatePluginTimeout() override;
#ifdef MOZ_ENABLE_PROFILER_SPS
void InitPluginProfiling();
void ShutdownPluginProfiling();
#endif
void RegisterSettingsCallbacks();
void UnregisterSettingsCallbacks();
@ -606,9 +590,6 @@ private:
// processes in existence!
dom::ContentParent* mContentParent;
nsCOMPtr<nsIObserver> mPluginOfflineObserver;
#ifdef MOZ_ENABLE_PROFILER_SPS
RefPtr<mozilla::ProfileGatherer> mGatherer;
#endif
nsCString mProfile;
bool mIsBlocklisted;
static bool sInstantiated;

View file

@ -2811,13 +2811,6 @@ WorkerThreadPrimaryRunnable::Run()
}
{
#ifdef MOZ_ENABLE_PROFILER_SPS
PseudoStack* stack = mozilla_get_pseudo_stack();
if (stack) {
stack->sampleContext(cx);
}
#endif
{
JSAutoRequest ar(cx);
@ -2829,12 +2822,6 @@ WorkerThreadPrimaryRunnable::Run()
}
BackgroundChild::CloseForCurrentThread();
#ifdef MOZ_ENABLE_PROFILER_SPS
if (stack) {
stack->sampleContext(nullptr);
}
#endif
}
// There may still be runnables on the debugger event queue that hold a

View file

@ -35,9 +35,6 @@
#include "TextRenderer.h" // for TextRenderer
#include <vector>
#include "GeckoProfiler.h" // for GeckoProfiler
#ifdef MOZ_ENABLE_PROFILER_SPS
#include "ProfilerMarkers.h" // for ProfilerMarkers
#endif
#define CULLING_LOG(...)
// #define CULLING_LOG(...) printf_stderr("CULLING: " __VA_ARGS__)
@ -86,26 +83,7 @@ static gfx::IntRect ContainerVisibleRect(ContainerT* aContainer)
static void PrintUniformityInfo(Layer* aLayer)
{
#ifdef MOZ_ENABLE_PROFILER_SPS
if (!profiler_is_active()) {
return;
}
// Don't want to print a log for smaller layers
if (aLayer->GetLocalVisibleRegion().GetBounds().width < 300 ||
aLayer->GetLocalVisibleRegion().GetBounds().height < 300) {
return;
}
Matrix4x4 transform = aLayer->AsLayerComposite()->GetShadowBaseTransform();
if (!transform.Is2D()) {
return;
}
Point translation = transform.As2D().GetTranslation();
LayerTranslationPayload* payload = new LayerTranslationPayload(aLayer, translation);
PROFILER_MARKER_PAYLOAD("LayerTranslation", payload);
#endif
/*** STUB ***/
}
/* all of the per-layer prepared data we need to maintain */

View file

@ -74,9 +74,6 @@
#include "mozilla/HalTypes.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/Telemetry.h"
#ifdef MOZ_ENABLE_PROFILER_SPS
#include "ProfilerMarkers.h"
#endif
#include "mozilla/VsyncDispatcher.h"
#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
#include "VsyncSource.h"
@ -1923,11 +1920,7 @@ CompositorBridgeParent::GetAPZCTreeManager(uint64_t aLayersId)
static void
InsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp)
{
#ifdef MOZ_ENABLE_PROFILER_SPS
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
VsyncPayload* payload = new VsyncPayload(aVsyncTimestamp);
PROFILER_MARKER_PAYLOAD("VsyncTimestamp", payload);
#endif
/*** STUB ***/
}
/*static */ void

View file

@ -73,9 +73,6 @@
#include "mozilla/HalTypes.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/Telemetry.h"
#ifdef MOZ_ENABLE_PROFILER_SPS
#include "ProfilerMarkers.h"
#endif
#include "mozilla/VsyncDispatcher.h"
#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
#include "VsyncSource.h"

View file

@ -73,24 +73,10 @@ protected:
MOZ_IS_CLASS_INIT
void Init() {
#ifdef MOZ_ENABLE_PROFILER_SPS
mPseudoStackHack = mozilla_get_pseudo_stack();
#endif
}
void CleanUp() {
#ifdef MOZ_ENABLE_PROFILER_SPS
mPseudoStackHack = nullptr;
#endif
}
private:
#ifdef MOZ_ENABLE_PROFILER_SPS
// This is needed to avoid a spurious leak report. There's no other
// use for it. See bug 1239504 and bug 1215265.
MOZ_INIT_OUTSIDE_CTOR PseudoStack* mPseudoStackHack;
#endif
};
}

View file

@ -5,9 +5,6 @@
#include "ContextStateTracker.h"
#include "GLContext.h"
#ifdef MOZ_ENABLE_PROFILER_SPS
#include "ProfilerMarkers.h"
#endif
namespace mozilla {
@ -110,15 +107,6 @@ ContextStateTrackerOGL::Flush(GLContext* aGL)
aGL->fDeleteQueries(1, &handle);
#ifdef MOZ_ENABLE_PROFILER_SPS
PROFILER_MARKER_PAYLOAD("gpu_timer_query", new GPUMarkerPayload(
mCompletedSections[0].mCpuTimeStart,
mCompletedSections[0].mCpuTimeEnd,
0,
gpuTime
));
#endif
mCompletedSections.RemoveElementAt(0);
}
}

View file

@ -1528,12 +1528,6 @@ XPCJSContext::~XPCJSContext()
delete mDyingWrappedNativeProtoMap;
mDyingWrappedNativeProtoMap = nullptr;
#ifdef MOZ_ENABLE_PROFILER_SPS
// Tell the profiler that the context is gone
if (PseudoStack* stack = mozilla_get_pseudo_stack())
stack->sampleContext(nullptr);
#endif
Preferences::UnregisterCallback(ReloadPrefsCallback, JS_OPTIONS_DOT_STR, this);
}
@ -3382,10 +3376,6 @@ XPCJSContext::Initialize()
JS_AddWeakPointerCompartmentCallback(cx, WeakPointerCompartmentCallback, this);
JS_SetWrapObjectCallbacks(cx, &WrapObjectCallbacks);
js::SetPreserveWrapperCallback(cx, PreserveWrapper);
#ifdef MOZ_ENABLE_PROFILER_SPS
if (PseudoStack* stack = mozilla_get_pseudo_stack())
stack->sampleContext(cx);
#endif
JS_SetAccumulateTelemetryCallback(cx, AccumulateTelemetryCallback);
js::SetActivityCallback(cx, ActivityCallback, this);
JS_AddInterruptCallback(cx, InterruptCallback);

View file

@ -61,9 +61,6 @@ RestyleTracker::Document() const {
struct RestyleEnumerateData : RestyleTracker::Hints {
RefPtr<dom::Element> mElement;
#if defined(MOZ_ENABLE_PROFILER_SPS)
UniquePtr<ProfilerBacktrace> mBacktrace;
#endif
};
inline void
@ -257,12 +254,6 @@ RestyleTracker::DoProcessRestyles()
data->mRestyleHint, MarkerTracingType::START)));
}
#if defined(MOZ_ENABLE_PROFILER_SPS)
Maybe<GeckoProfilerTracingRAII> profilerRAII;
if (profiler_feature_active("restyle")) {
profilerRAII.emplace("Paint", "Styles", Move(data->mBacktrace));
}
#endif
ProcessOneRestyle(element, data->mRestyleHint, data->mChangeHint,
data->mRestyleHintData);
AddRestyleRootsIfAwaitingRestyle(data->mDescendants);
@ -337,9 +328,6 @@ RestyleTracker::DoProcessRestyles()
// We can move data since we'll be clearing mPendingRestyles after
// we finish enumerating it.
restyle->mRestyleHintData = Move(data->mRestyleHintData);
#if defined(MOZ_ENABLE_PROFILER_SPS)
restyle->mBacktrace = Move(data->mBacktrace);
#endif
#ifdef RESTYLE_LOGGING
count++;
@ -365,12 +353,6 @@ RestyleTracker::DoProcessRestyles()
index++, count);
LOG_RESTYLE_INDENT();
#if defined(MOZ_ENABLE_PROFILER_SPS)
Maybe<GeckoProfilerTracingRAII> profilerRAII;
if (profiler_feature_active("restyle")) {
profilerRAII.emplace("Paint", "Styles", Move(currentRestyle->mBacktrace));
}
#endif
if (isTimelineRecording) {
timelines->AddMarkerForDocShell(docShell, Move(
MakeUnique<RestyleTimelineMarker>(

View file

@ -22,10 +22,6 @@
#include "GeckoProfiler.h"
#include "mozilla/Maybe.h"
#if defined(MOZ_ENABLE_PROFILER_SPS)
#include "ProfilerBacktrace.h"
#endif
namespace mozilla {
class RestyleManager;
@ -128,9 +124,6 @@ public:
// that we called AddPendingRestyle for and found the element this is
// the RestyleData for as its nearest restyle root.
nsTArray<RefPtr<Element>> mDescendants;
#if defined(MOZ_ENABLE_PROFILER_SPS)
UniquePtr<ProfilerBacktrace> mBacktrace;
#endif
};
/**
@ -265,11 +258,6 @@ RestyleTracker::AddPendingRestyleToTable(Element* aElement,
if (!existingData) {
RestyleData* rd =
new RestyleData(aRestyleHint, aMinChangeHint, aRestyleHintData);
#if defined(MOZ_ENABLE_PROFILER_SPS)
if (profiler_feature_active("restyle")) {
rd->mBacktrace.reset(profiler_get_backtrace());
}
#endif
mPendingRestyles.Put(aElement, rd);
return false;
}

View file

@ -4064,23 +4064,6 @@ PresShell::FlushPendingNotifications(mozilla::ChangesToFlush aFlush)
*/
mozFlushType flushType = aFlush.mFlushType;
#ifdef MOZ_ENABLE_PROFILER_SPS
static const char flushTypeNames[][20] = {
"Content",
"ContentAndNotify",
"Style",
"InterruptibleLayout",
"Layout",
"Display"
};
// Make sure that we don't miss things added to mozFlushType!
MOZ_ASSERT(static_cast<uint32_t>(flushType) <= ArrayLength(flushTypeNames));
PROFILER_LABEL_PRINTF("PresShell", "Flush",
js::ProfileEntry::Category::GRAPHICS, "(Flush_%s)", flushTypeNames[flushType - 1]);
#endif
#ifdef ACCESSIBILITY
#ifdef DEBUG
nsAccessibilityService* accService = GetAccService();

View file

@ -174,9 +174,6 @@
@BINPATH@/components/layout_xul.xpt
@BINPATH@/components/locale.xpt
@BINPATH@/components/lwbrk.xpt
#ifdef MOZ_ENABLE_PROFILER_SPS
@BINPATH@/components/memory_profiler.xpt
#endif
@BINPATH@/components/mimetype.xpt
@BINPATH@/components/mozfind.xpt
@BINPATH@/components/necko_about.xpt
@ -206,9 +203,6 @@
@BINPATH@/components/plugin.xpt
@BINPATH@/components/pref.xpt
@BINPATH@/components/prefetch.xpt
#ifdef MOZ_ENABLE_PROFILER_SPS
@BINPATH@/components/profiler.xpt
#endif
@BINPATH@/components/rdf.xpt
@BINPATH@/components/satchel.xpt
@BINPATH@/components/saxparser.xpt

View file

@ -110,18 +110,6 @@ public:
// We don't report SQLite I/O on Windows because we have a comprehensive
// mechanism for intercepting I/O on that platform that captures a superset
// of the data captured here.
#if defined(MOZ_ENABLE_PROFILER_SPS) && !defined(XP_WIN)
if (IOInterposer::IsObservedOperation(op)) {
const char* main_ref = "sqlite-mainthread";
const char* other_ref = "sqlite-otherthread";
// Create observation
IOInterposeObserver::Observation ob(op, start, end,
(mainThread ? main_ref : other_ref));
// Report observation
IOInterposer::Report(ob);
}
#endif /* defined(MOZ_ENABLE_PROFILER_SPS) && !defined(XP_WIN) */
}
private:

View file

@ -73,9 +73,6 @@
#include "mozilla/PoisonIOInterposer.h"
#include "mozilla/StartupTimeline.h"
#include "mozilla/HangMonitor.h"
#if defined(MOZ_ENABLE_PROFILER_SPS)
#include "shared-libraries.h"
#endif
namespace {
@ -682,13 +679,6 @@ public:
static void ShutdownTelemetry();
static void RecordSlowStatement(const nsACString &sql, const nsACString &dbName,
uint32_t delay);
#if defined(MOZ_ENABLE_PROFILER_SPS)
static void RecordChromeHang(uint32_t aDuration,
Telemetry::ProcessedStack &aStack,
int32_t aSystemUptime,
int32_t aFirefoxUptime,
HangAnnotationsPtr aAnnotations);
#endif
static void RecordThreadHangStats(Telemetry::ThreadHangStats& aStats);
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
struct Stat {
@ -2224,31 +2214,6 @@ TelemetryImpl::RecordIceCandidates(const uint32_t iceCandidateBitmask,
sTelemetry->mWebrtcTelemetry.RecordIceCandidateMask(iceCandidateBitmask, success);
}
#if defined(MOZ_ENABLE_PROFILER_SPS)
void
TelemetryImpl::RecordChromeHang(uint32_t aDuration,
Telemetry::ProcessedStack &aStack,
int32_t aSystemUptime,
int32_t aFirefoxUptime,
HangAnnotationsPtr aAnnotations)
{
if (!sTelemetry || !TelemetryHistogram::CanRecordExtended())
return;
HangAnnotationsPtr annotations;
// We only pass aAnnotations if it is not empty.
if (aAnnotations && !aAnnotations->IsEmpty()) {
annotations = Move(aAnnotations);
}
MutexAutoLock hangReportMutex(sTelemetry->mHangReportsMutex);
sTelemetry->mHangReports.AddHang(aStack, aDuration,
aSystemUptime, aFirefoxUptime,
Move(annotations));
}
#endif
void
TelemetryImpl::RecordThreadHangStats(Telemetry::ThreadHangStats& aStats)
{
@ -2452,18 +2417,6 @@ struct StackFrame
uint16_t mModIndex; // The index of module that has this program counter.
};
#ifdef MOZ_ENABLE_PROFILER_SPS
static bool CompareByPC(const StackFrame &a, const StackFrame &b)
{
return a.mPC < b.mPC;
}
static bool CompareByIndex(const StackFrame &a, const StackFrame &b)
{
return a.mIndex < b.mIndex;
}
#endif
} // namespace
@ -2629,60 +2582,6 @@ GetStackAndModules(const std::vector<uintptr_t>& aPCs)
rawStack.push_back(Frame);
}
#ifdef MOZ_ENABLE_PROFILER_SPS
// Remove all modules not referenced by a PC on the stack
std::sort(rawStack.begin(), rawStack.end(), CompareByPC);
size_t moduleIndex = 0;
size_t stackIndex = 0;
size_t stackSize = rawStack.size();
SharedLibraryInfo rawModules = SharedLibraryInfo::GetInfoForSelf();
rawModules.SortByAddress();
while (moduleIndex < rawModules.GetSize()) {
const SharedLibrary& module = rawModules.GetEntry(moduleIndex);
uintptr_t moduleStart = module.GetStart();
uintptr_t moduleEnd = module.GetEnd() - 1;
// the interval is [moduleStart, moduleEnd)
bool moduleReferenced = false;
for (;stackIndex < stackSize; ++stackIndex) {
uintptr_t pc = rawStack[stackIndex].mPC;
if (pc >= moduleEnd)
break;
if (pc >= moduleStart) {
// If the current PC is within the current module, mark
// module as used
moduleReferenced = true;
rawStack[stackIndex].mPC -= moduleStart;
rawStack[stackIndex].mModIndex = moduleIndex;
} else {
// PC does not belong to any module. It is probably from
// the JIT. Use a fixed mPC so that we don't get different
// stacks on different runs.
rawStack[stackIndex].mPC =
std::numeric_limits<uintptr_t>::max();
}
}
if (moduleReferenced) {
++moduleIndex;
} else {
// Remove module if no PCs within its address range
rawModules.RemoveEntries(moduleIndex, moduleIndex + 1);
}
}
for (;stackIndex < stackSize; ++stackIndex) {
// These PCs are past the last module.
rawStack[stackIndex].mPC = std::numeric_limits<uintptr_t>::max();
}
std::sort(rawStack.begin(), rawStack.end(), CompareByIndex);
#endif
// Copy the information to the return value.
ProcessedStack Ret;
for (std::vector<StackFrame>::iterator i = rawStack.begin(),
@ -2692,28 +2591,6 @@ GetStackAndModules(const std::vector<uintptr_t>& aPCs)
Ret.AddFrame(frame);
}
#ifdef MOZ_ENABLE_PROFILER_SPS
for (unsigned i = 0, n = rawModules.GetSize(); i != n; ++i) {
const SharedLibrary &info = rawModules.GetEntry(i);
const std::string &name = info.GetName();
std::string basename = name;
#ifdef XP_MACOSX
// FIXME: We want to use just the basename as the libname, but the
// current profiler addon needs the full path name, so we compute the
// basename in here.
size_t pos = name.rfind('/');
if (pos != std::string::npos) {
basename = name.substr(pos + 1);
}
#endif
mozilla::Telemetry::ProcessedStack::Module module = {
basename,
info.GetBreakpadId()
};
Ret.AddModule(module);
}
#endif
return Ret;
}
@ -2910,19 +2787,6 @@ void Init()
MOZ_ASSERT(telemetryService);
}
#if defined(MOZ_ENABLE_PROFILER_SPS)
void RecordChromeHang(uint32_t duration,
ProcessedStack &aStack,
int32_t aSystemUptime,
int32_t aFirefoxUptime,
HangAnnotationsPtr aAnnotations)
{
TelemetryImpl::RecordChromeHang(duration, aStack,
aSystemUptime, aFirefoxUptime,
Move(aAnnotations));
}
#endif
void RecordThreadHangStats(ThreadHangStats& aStats)
{
TelemetryImpl::RecordThreadHangStats(aStats);

View file

@ -311,25 +311,6 @@ const uint32_t kSlowSQLThresholdForMainThread = 50;
const uint32_t kSlowSQLThresholdForHelperThreads = 100;
class ProcessedStack;
/**
* Record the main thread's call stack after it hangs.
*
* @param aDuration - Approximate duration of main thread hang, in seconds
* @param aStack - Array of PCs from the hung call stack
* @param aSystemUptime - System uptime at the time of the hang, in minutes
* @param aFirefoxUptime - Firefox uptime at the time of the hang, in minutes
* @param aAnnotations - Any annotations to be added to the report
*/
#if defined(MOZ_ENABLE_PROFILER_SPS)
void RecordChromeHang(uint32_t aDuration,
ProcessedStack &aStack,
int32_t aSystemUptime,
int32_t aFirefoxUptime,
mozilla::UniquePtr<mozilla::HangMonitor::HangAnnotations>
aAnnotations);
#endif
class ThreadHangStats;
/**

View file

@ -257,13 +257,6 @@ MOZ_SAFE_BROWSING:
false,
#endif
MOZ_ENABLE_PROFILER_SPS:
#ifdef MOZ_ENABLE_PROFILER_SPS
true,
#else
false,
#endif
MOZ_ANDROID_ACTIVITY_STREAM:
#ifdef MOZ_ANDROID_ACTIVITY_STREAM
true,

View file

@ -72,9 +72,6 @@ var initTable = [
["obs", "@mozilla.org/observer-service;1", "nsIObserverService"],
["perms", "@mozilla.org/permissionmanager;1", "nsIPermissionManager"],
["prompt", "@mozilla.org/embedcomp/prompt-service;1", "nsIPromptService"],
#ifdef MOZ_ENABLE_PROFILER_SPS
["profiler", "@mozilla.org/tools/profiler;1", "nsIProfiler"],
#endif
["scriptloader", "@mozilla.org/moz/jssubscript-loader;1", "mozIJSSubScriptLoader"],
["scriptSecurityManager", "@mozilla.org/scriptsecuritymanager;1", "nsIScriptSecurityManager"],
#ifdef MOZ_TOOLKIT_SEARCH

View file

@ -22,23 +22,6 @@ def systrace(value, target):
set_define('MOZ_USE_SYSTRACE', systrace)
@depends(target)
def sps_profiler(target):
if target.os == 'Android':
return target.cpu in ('arm', 'x86')
elif target.kernel == 'Linux':
return target.cpu in ('x86', 'x86_64')
return target.os in ('OSX', 'WINNT')
@depends(sps_profiler)
def sps_profiler_define(value):
if value:
return True
set_config('MOZ_ENABLE_PROFILER_SPS', sps_profiler_define)
set_define('MOZ_ENABLE_PROFILER_SPS', sps_profiler_define)
# JACK cubeb backend
# ==============================================================
option('--enable-jack', env='MOZ_JACK',

View file

@ -1,168 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=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 "GCHeapProfilerImpl.h"
#include "UncensoredAllocator.h"
namespace mozilla {
GCHeapProfilerImpl::GCHeapProfilerImpl()
{
mLock = PR_NewLock();
mMarking = false;
}
GCHeapProfilerImpl::~GCHeapProfilerImpl()
{
if (mLock) {
PR_DestroyLock(mLock);
}
}
nsTArray<nsCString>
GCHeapProfilerImpl::GetNames() const
{
return mTraceTable.GetNames();
}
nsTArray<TrieNode>
GCHeapProfilerImpl::GetTraces() const
{
return mTraceTable.GetTraces();
}
const nsTArray<AllocEvent>&
GCHeapProfilerImpl::GetEvents() const
{
return mAllocEvents;
}
void
GCHeapProfilerImpl::reset()
{
mTraceTable.Reset();
mAllocEvents.Clear();
mNurseryEntries.Clear();
mTenuredEntriesFG.Clear();
mTenuredEntriesBG.Clear();
}
void
GCHeapProfilerImpl::sampleTenured(void* addr, uint32_t size)
{
SampleInternal(addr, size, mTenuredEntriesFG);
}
void
GCHeapProfilerImpl::sampleNursery(void* addr, uint32_t size)
{
SampleInternal(addr, size, mNurseryEntries);
}
void
GCHeapProfilerImpl::markTenuredStart()
{
AutoUseUncensoredAllocator ua;
AutoMPLock lock(mLock);
if (!mMarking) {
mMarking = true;
mTenuredEntriesFG.SwapElements(mTenuredEntriesBG);
MOZ_ASSERT(mTenuredEntriesFG.Count() == 0);
}
}
void
GCHeapProfilerImpl::markTenured(void* addr)
{
AutoUseUncensoredAllocator ua;
AutoMPLock lock(mLock);
if (mMarking) {
AllocEntry entry;
if (mTenuredEntriesBG.Get(addr, &entry)) {
entry.mMarked = true;
mTenuredEntriesBG.Put(addr, entry);
}
}
}
void
GCHeapProfilerImpl::sweepTenured()
{
AutoUseUncensoredAllocator ua;
AutoMPLock lock(mLock);
if (mMarking) {
mMarking = false;
for (auto iter = mTenuredEntriesBG.Iter(); !iter.Done(); iter.Next()) {
if (iter.Data().mMarked) {
iter.Data().mMarked = false;
mTenuredEntriesFG.Put(iter.Key(), iter.Data());
} else {
AllocEvent& oldEvent = mAllocEvents[iter.Data().mEventIdx];
AllocEvent newEvent(oldEvent.mTraceIdx, -oldEvent.mSize, TimeStamp::Now());
mAllocEvents.AppendElement(newEvent);
}
}
mTenuredEntriesBG.Clear();
}
}
void
GCHeapProfilerImpl::sweepNursery()
{
AutoUseUncensoredAllocator ua;
AutoMPLock lock(mLock);
for (auto iter = mNurseryEntries.Iter(); !iter.Done(); iter.Next()) {
AllocEvent& oldEvent = mAllocEvents[iter.Data().mEventIdx];
AllocEvent newEvent(oldEvent.mTraceIdx, -oldEvent.mSize, TimeStamp::Now());
mAllocEvents.AppendElement(newEvent);
}
mNurseryEntries.Clear();
}
void
GCHeapProfilerImpl::moveNurseryToTenured(void* addrOld, void* addrNew)
{
AutoUseUncensoredAllocator ua;
AutoMPLock lock(mLock);
AllocEntry entryOld;
if (!mNurseryEntries.Get(addrOld, &entryOld)) {
return;
}
// Because the tenured heap is sampled, the address might already be there.
// If not, the address is inserted with the old event.
AllocEntry tenuredEntryOld;
if (!mTenuredEntriesFG.Get(addrNew, &tenuredEntryOld)) {
mTenuredEntriesFG.Put(addrNew, AllocEntry(entryOld.mEventIdx));
} else {
// If it is already inserted, the insertion above will fail and the
// iterator of the already-inserted element is returned.
// We choose to ignore the the new event by setting its size zero and point
// the newly allocated address to the old event.
// An event of size zero will be skipped when reporting.
mAllocEvents[entryOld.mEventIdx].mSize = 0;
tenuredEntryOld.mEventIdx = entryOld.mEventIdx;
mTenuredEntriesFG.Put(addrNew, tenuredEntryOld);
}
mNurseryEntries.Remove(addrOld);
}
void
GCHeapProfilerImpl::SampleInternal(void* aAddr, uint32_t aSize, AllocMap& aTable)
{
AutoUseUncensoredAllocator ua;
AutoMPLock lock(mLock);
size_t nSamples = AddBytesSampled(aSize);
if (nSamples > 0) {
nsTArray<nsCString> trace = GetStacktrace();
AllocEvent ai(mTraceTable.Insert(trace), nSamples * mSampleSize, TimeStamp::Now());
aTable.Put(aAddr, AllocEntry(mAllocEvents.Length()));
mAllocEvents.AppendElement(ai);
}
}
} // namespace mozilla

View file

@ -1,53 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=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/. */
#ifndef memory_profiler_GCHeapProfilerImpl_h
#define memory_profiler_GCHeapProfilerImpl_h
#include "CompactTraceTable.h"
#include "MemoryProfiler.h"
#include "jsfriendapi.h"
namespace mozilla {
class GCHeapProfilerImpl final : public GCHeapProfiler
, public ProfilerImpl
{
public:
GCHeapProfilerImpl();
~GCHeapProfilerImpl() override;
nsTArray<nsCString> GetNames() const override;
nsTArray<TrieNode> GetTraces() const override;
const nsTArray<AllocEvent>& GetEvents() const override;
void reset() override;
void sampleTenured(void* addr, uint32_t size) override;
void sampleNursery(void* addr, uint32_t size) override;
void markTenuredStart() override;
void markTenured(void* addr) override;
void sweepTenured() override;
void sweepNursery() override;
void moveNurseryToTenured(void* addrOld, void* addrNew) override;
private:
void SampleInternal(void* addr, uint32_t size, AllocMap& table);
PRLock* mLock;
bool mMarking;
AllocMap mNurseryEntries;
AllocMap mTenuredEntriesFG;
AllocMap mTenuredEntriesBG;
nsTArray<AllocEvent> mAllocEvents;
CompactTraceTable mTraceTable;
};
} // namespace mozilla
#endif // memory_profiler_GCHeapProfilerImpl_h

View file

@ -1,324 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=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 "MemoryProfiler.h"
#include <cmath>
#include <cstdlib>
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Move.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/UniquePtr.h"
#include "GCHeapProfilerImpl.h"
#include "GeckoProfiler.h"
#include "NativeProfilerImpl.h"
#include "UncensoredAllocator.h"
#include "js/TypeDecls.h"
#include "jsfriendapi.h"
#include "nsIDOMClassInfo.h"
#include "nsIGlobalObject.h"
#include "prtime.h"
#include "xpcprivate.h"
namespace mozilla {
#define MEMORY_PROFILER_SAMPLE_SIZE 65536
#define BACKTRACE_BUFFER_SIZE 16384
ProfilerImpl::ProfilerImpl()
: mSampleSize(MEMORY_PROFILER_SAMPLE_SIZE)
{
mLog1minusP = std::log(1.0 - 1.0 / mSampleSize);
mRemainingBytes = std::floor(std::log(1.0 - DRandom()) / mLog1minusP);
}
nsTArray<nsCString>
ProfilerImpl::GetStacktrace()
{
nsTArray<nsCString> trace;
auto output = MakeUnique<char[]>(BACKTRACE_BUFFER_SIZE);
profiler_get_backtrace_noalloc(output.get(), BACKTRACE_BUFFER_SIZE);
for (const char* p = output.get(); *p; p += strlen(p) + 1) {
trace.AppendElement()->Assign(p);
}
return trace;
}
// Generate a random number in [0, 1).
double
ProfilerImpl::DRandom()
{
return double(((uint64_t(std::rand()) & ((1 << 26) - 1)) << 27) +
(uint64_t(std::rand()) & ((1 << 27) - 1)))
/ (uint64_t(1) << 53);
}
size_t
ProfilerImpl::AddBytesSampled(uint32_t aBytes)
{
size_t nSamples = 0;
while (mRemainingBytes <= aBytes) {
mRemainingBytes += std::floor(std::log(1.0 - DRandom()) / mLog1minusP);
nSamples++;
}
mRemainingBytes -= aBytes;
return nSamples;
}
NS_IMPL_ISUPPORTS(MemoryProfiler, nsIMemoryProfiler)
PRLock* MemoryProfiler::sLock;
uint32_t MemoryProfiler::sProfileContextCount;
StaticAutoPtr<NativeProfilerImpl> MemoryProfiler::sNativeProfiler;
StaticAutoPtr<JSContextProfilerMap> MemoryProfiler::sJSContextProfilerMap;
TimeStamp MemoryProfiler::sStartTime;
void
MemoryProfiler::InitOnce()
{
MOZ_ASSERT(NS_IsMainThread());
static bool initialized = false;
if (!initialized) {
MallocHook::Initialize();
sLock = PR_NewLock();
sProfileContextCount = 0;
sJSContextProfilerMap = new JSContextProfilerMap();
ClearOnShutdown(&sJSContextProfilerMap);
ClearOnShutdown(&sNativeProfiler);
std::srand(PR_Now());
bool ignored;
sStartTime = TimeStamp::ProcessCreation(ignored);
initialized = true;
}
}
NS_IMETHODIMP
MemoryProfiler::StartProfiler()
{
InitOnce();
AutoUseUncensoredAllocator ua;
AutoMPLock lock(sLock);
JSContext* context = XPCJSContext::Get()->Context();
ProfilerForJSContext profiler;
if (!sJSContextProfilerMap->Get(context, &profiler) ||
!profiler.mEnabled) {
if (sProfileContextCount == 0) {
js::EnableContextProfilingStack(context, true);
if (!sNativeProfiler) {
sNativeProfiler = new NativeProfilerImpl();
}
MemProfiler::SetNativeProfiler(sNativeProfiler);
}
GCHeapProfilerImpl* gp = new GCHeapProfilerImpl();
profiler.mEnabled = true;
profiler.mProfiler = gp;
sJSContextProfilerMap->Put(context, profiler);
MemProfiler::GetMemProfiler(context)->start(gp);
if (sProfileContextCount == 0) {
MallocHook::Enable(sNativeProfiler);
}
sProfileContextCount++;
}
return NS_OK;
}
NS_IMETHODIMP
MemoryProfiler::StopProfiler()
{
InitOnce();
AutoUseUncensoredAllocator ua;
AutoMPLock lock(sLock);
JSContext* context = XPCJSContext::Get()->Context();
ProfilerForJSContext profiler;
if (sJSContextProfilerMap->Get(context, &profiler) &&
profiler.mEnabled) {
MemProfiler::GetMemProfiler(context)->stop();
if (--sProfileContextCount == 0) {
MallocHook::Disable();
MemProfiler::SetNativeProfiler(nullptr);
js::EnableContextProfilingStack(context, false);
}
profiler.mEnabled = false;
sJSContextProfilerMap->Put(context, profiler);
}
return NS_OK;
}
NS_IMETHODIMP
MemoryProfiler::ResetProfiler()
{
InitOnce();
AutoUseUncensoredAllocator ua;
AutoMPLock lock(sLock);
JSContext* context = XPCJSContext::Get()->Context();
ProfilerForJSContext profiler;
if (!sJSContextProfilerMap->Get(context, &profiler) ||
!profiler.mEnabled) {
delete profiler.mProfiler;
profiler.mProfiler = nullptr;
sJSContextProfilerMap->Put(context, profiler);
}
if (sProfileContextCount == 0) {
sNativeProfiler = nullptr;
}
return NS_OK;
}
struct MergedTraces
{
nsTArray<nsCString> mNames;
nsTArray<TrieNode> mTraces;
nsTArray<AllocEvent> mEvents;
};
// Merge events and corresponding traces and names.
static MergedTraces
MergeResults(const nsTArray<nsCString>& names0,
const nsTArray<TrieNode>& traces0,
const nsTArray<AllocEvent>& events0,
const nsTArray<nsCString>& names1,
const nsTArray<TrieNode>& traces1,
const nsTArray<AllocEvent>& events1)
{
NodeIndexMap<nsCStringHashKey, nsCString> names;
NodeIndexMap<nsGenericHashKey<TrieNode>, TrieNode> traces;
nsTArray<AllocEvent> events;
nsTArray<size_t> names1Tonames0(names1.Length());
nsTArray<size_t> traces1Totraces0(traces1.Length());
// Merge names.
for (auto& i: names0) {
names.Insert(i);
}
for (auto& i: names1) {
names1Tonames0.AppendElement(names.Insert(i));
}
// Merge traces. Note that traces1[i].parentIdx < i for all i > 0.
for (auto& i: traces0) {
traces.Insert(i);
}
traces1Totraces0.AppendElement(0);
for (size_t i = 1; i < traces1.Length(); i++) {
TrieNode node = traces1[i];
node.parentIdx = traces1Totraces0[node.parentIdx];
node.nameIdx = names1Tonames0[node.nameIdx];
traces1Totraces0.AppendElement(traces.Insert(node));
}
// Merge the events according to timestamps.
auto p0 = events0.begin();
auto p1 = events1.begin();
while (p0 != events0.end() && p1 != events1.end()) {
if (p0->mTimestamp < p1->mTimestamp) {
events.AppendElement(*p0++);
} else {
events.AppendElement(*p1++);
events.LastElement().mTraceIdx =
traces1Totraces0[events.LastElement().mTraceIdx];
}
}
while (p0 != events0.end()) {
events.AppendElement(*p0++);
}
while (p1 != events1.end()) {
events.AppendElement(*p1++);
events.LastElement().mTraceIdx =
traces1Totraces0[events.LastElement().mTraceIdx];
}
return MergedTraces{names.Serialize(), traces.Serialize(), Move(events)};
}
NS_IMETHODIMP
MemoryProfiler::GetResults(JSContext* cx, JS::MutableHandle<JS::Value> aResult)
{
InitOnce();
AutoUseUncensoredAllocator ua;
AutoMPLock lock(sLock);
JSContext* context = XPCJSContext::Get()->Context();
// Getting results when the profiler is running is not allowed.
if (sProfileContextCount > 0) {
return NS_OK;
}
// Return immediately when native profiler does not exist.
if (!sNativeProfiler) {
return NS_OK;
}
// Return immediately when there's no result in current context.
ProfilerForJSContext profiler;
if (!sJSContextProfilerMap->Get(context, &profiler) ||
!profiler.mProfiler) {
return NS_OK;
}
GCHeapProfilerImpl* gp = profiler.mProfiler;
auto results = MergeResults(gp->GetNames(), gp->GetTraces(), gp->GetEvents(),
sNativeProfiler->GetNames(),
sNativeProfiler->GetTraces(),
sNativeProfiler->GetEvents());
const nsTArray<nsCString>& names = results.mNames;
const nsTArray<TrieNode>& traces = results.mTraces;
const nsTArray<AllocEvent>& events = results.mEvents;
JS::RootedObject jsnames(cx, JS_NewArrayObject(cx, names.Length()));
JS::RootedObject jstraces(cx, JS_NewArrayObject(cx, traces.Length()));
JS::RootedObject jsevents(cx, JS_NewArrayObject(cx, events.Length()));
for (size_t i = 0; i < names.Length(); i++) {
JS::RootedString name(cx, JS_NewStringCopyZ(cx, names[i].get()));
JS_SetElement(cx, jsnames, i, name);
}
for (size_t i = 0; i < traces.Length(); i++) {
JS::RootedObject tn(cx, JS_NewPlainObject(cx));
JS::RootedValue nameIdx(cx, JS_NumberValue(traces[i].nameIdx));
JS::RootedValue parentIdx(cx, JS_NumberValue(traces[i].parentIdx));
JS_SetProperty(cx, tn, "nameIdx", nameIdx);
JS_SetProperty(cx, tn, "parentIdx", parentIdx);
JS_SetElement(cx, jstraces, i, tn);
}
int i = 0;
for (auto ent: events) {
if (ent.mSize == 0) {
continue;
}
MOZ_ASSERT(!sStartTime.IsNull());
double time = (ent.mTimestamp - sStartTime).ToMilliseconds();
JS::RootedObject tn(cx, JS_NewPlainObject(cx));
JS::RootedValue size(cx, JS_NumberValue(ent.mSize));
JS::RootedValue traceIdx(cx, JS_NumberValue(ent.mTraceIdx));
JS::RootedValue timestamp(cx, JS_NumberValue(time));
JS_SetProperty(cx, tn, "size", size);
JS_SetProperty(cx, tn, "traceIdx", traceIdx);
JS_SetProperty(cx, tn, "timestamp", timestamp);
JS_SetElement(cx, jsevents, i++, tn);
}
JS_SetArrayLength(cx, jsevents, i);
JS::RootedObject result(cx, JS_NewPlainObject(cx));
JS::RootedValue objnames(cx, ObjectOrNullValue(jsnames));
JS_SetProperty(cx, result, "names", objnames);
JS::RootedValue objtraces(cx, ObjectOrNullValue(jstraces));
JS_SetProperty(cx, result, "traces", objtraces);
JS::RootedValue objevents(cx, ObjectOrNullValue(jsevents));
JS_SetProperty(cx, result, "events", objevents);
aResult.setObject(*result);
return NS_OK;
}
} // namespace mozilla

View file

@ -1,159 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=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/. */
#ifndef tools_profiler_MemoryProfiler_h
#define tools_profiler_MemoryProfiler_h
#include "nsIMemoryProfiler.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/TimeStamp.h"
#include "CompactTraceTable.h"
#include "nsTArray.h"
#include "prlock.h"
#define MEMORY_PROFILER_CID \
{ 0xf976eaa2, 0xcc1f, 0x47ee, \
{ 0x81, 0x29, 0xb8, 0x26, 0x2a, 0x3d, 0xb6, 0xb2 } }
#define MEMORY_PROFILER_CONTRACT_ID "@mozilla.org/tools/memory-profiler;1"
struct PRLock;
namespace mozilla {
class NativeProfilerImpl;
class GCHeapProfilerImpl;
struct ProfilerForJSContext
{
ProfilerForJSContext()
: mProfiler(nullptr)
, mEnabled(false)
{}
GCHeapProfilerImpl* mProfiler;
bool mEnabled;
};
using JSContextProfilerMap =
nsDataHashtable<nsClearingPtrHashKey<JSContext>, ProfilerForJSContext>;
class MemoryProfiler final : public nsIMemoryProfiler
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIMEMORYPROFILER
private:
static void InitOnce();
~MemoryProfiler() {}
// The accesses to other static member are guarded by sLock and
// sProfileContextCount.
static PRLock* sLock;
static uint32_t sProfileContextCount;
static StaticAutoPtr<NativeProfilerImpl> sNativeProfiler;
static StaticAutoPtr<JSContextProfilerMap> sJSContextProfilerMap;
static TimeStamp sStartTime;
};
// Allocation events to be reported.
struct AllocEvent {
TimeStamp mTimestamp;
// index to a stacktrace singleton.
uint32_t mTraceIdx;
// Allocation size
int32_t mSize;
AllocEvent(uint32_t aTraceIdx, int32_t aSize, TimeStamp aTimestamp)
: mTimestamp(aTimestamp)
, mTraceIdx(aTraceIdx)
, mSize(aSize)
{}
};
// Index to allocation events but also a mark bit to be GC-able.
struct AllocEntry {
uint32_t mEventIdx : 31;
bool mMarked : 1;
// Default constructor for uninitialized stack value required by
// getter methods.
AllocEntry()
: mEventIdx(0)
, mMarked(false)
{}
explicit AllocEntry(int aEventIdx)
: mEventIdx(aEventIdx)
, mMarked(false)
{}
};
using AllocMap = nsDataHashtable<nsClearingVoidPtrHashKey, AllocEntry>;
class ProfilerImpl
{
public:
static nsTArray<nsCString> GetStacktrace();
static double DRandom();
ProfilerImpl();
virtual nsTArray<nsCString> GetNames() const = 0;
virtual nsTArray<TrieNode> GetTraces() const = 0;
virtual const nsTArray<AllocEvent>& GetEvents() const = 0;
protected:
/**
* The sampler generates a random variable which conforms to a geometric
* distribution of probability p = 1 / mSampleSize to calculate the
* next-to-be-sampled byte directly; It avoids rolling a dice on each byte.
*
* Let Bn denote a Bernoulli process with first success on n-th trial, the
* cumulative distribution function of Bn is Cn = 1 - (1 - p) ^ n.
* Let U denote a uniformly distributed random variable in [0, 1).
* A geometric random variable can be generated by Cn's reverse function:
* G = floor(log(1 - U) / log(1 - p)).
*
* @param aSize the number of bytes seen
* @return the number of events sampled
*/
size_t AddBytesSampled(uint32_t aBytes);
uint32_t mSampleSize;
private:
uint32_t mRemainingBytes;
double mLog1minusP;
};
/*
* This class is used to make sure the profile data is only accessed
* on one thread at a time. Don't use mozilla::Mutex because we don't
* want to allocate memory.
*/
class AutoMPLock
{
public:
explicit AutoMPLock(PRLock* aLock)
{
MOZ_ASSERT(aLock);
mLock = aLock;
PR_Lock(mLock);
}
~AutoMPLock()
{
PR_Unlock(mLock);
}
private:
PRLock* mLock;
};
} // namespace mozilla
#endif

View file

@ -1,82 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=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 "NativeProfilerImpl.h"
#include "UncensoredAllocator.h"
namespace mozilla {
NativeProfilerImpl::NativeProfilerImpl()
{
mLock = PR_NewLock();
}
NativeProfilerImpl::~NativeProfilerImpl()
{
if (mLock) {
PR_DestroyLock(mLock);
}
}
nsTArray<nsCString>
NativeProfilerImpl::GetNames() const
{
return mTraceTable.GetNames();
}
nsTArray<TrieNode>
NativeProfilerImpl::GetTraces() const
{
return mTraceTable.GetTraces();
}
const nsTArray<AllocEvent>&
NativeProfilerImpl::GetEvents() const
{
return mAllocEvents;
}
void
NativeProfilerImpl::reset()
{
mTraceTable.Reset();
mAllocEvents.Clear();
mNativeEntries.Clear();
}
void
NativeProfilerImpl::sampleNative(void* addr, uint32_t size)
{
AutoUseUncensoredAllocator ua;
AutoMPLock lock(mLock);
size_t nSamples = AddBytesSampled(size);
if (nSamples > 0) {
nsTArray<nsCString> trace = GetStacktrace();
AllocEvent ai(mTraceTable.Insert(trace), nSamples * mSampleSize, TimeStamp::Now());
mNativeEntries.Put(addr, AllocEntry(mAllocEvents.Length()));
mAllocEvents.AppendElement(ai);
}
}
void
NativeProfilerImpl::removeNative(void* addr)
{
AutoUseUncensoredAllocator ua;
AutoMPLock lock(mLock);
AllocEntry entry;
if (!mNativeEntries.Get(addr, &entry)) {
return;
}
AllocEvent& oldEvent = mAllocEvents[entry.mEventIdx];
AllocEvent newEvent(oldEvent.mTraceIdx, -oldEvent.mSize, TimeStamp::Now());
mAllocEvents.AppendElement(newEvent);
mNativeEntries.Remove(addr);
}
} // namespace mozilla

View file

@ -1,43 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=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/. */
#ifndef memory_profiler_NativeProfilerImpl_h
#define memory_profiler_NativeProfilerImpl_h
#include "CompactTraceTable.h"
#include "MemoryProfiler.h"
#include "jsfriendapi.h"
struct PRLock;
namespace mozilla {
class NativeProfilerImpl final : public NativeProfiler
, public ProfilerImpl
{
public:
NativeProfilerImpl();
~NativeProfilerImpl() override;
nsTArray<nsCString> GetNames() const override;
nsTArray<TrieNode> GetTraces() const override;
const nsTArray<AllocEvent>& GetEvents() const override;
void reset() override;
void sampleNative(void* addr, uint32_t size) override;
void removeNative(void* addr) override;
private:
PRLock* mLock;
AllocMap mNativeEntries;
nsTArray<AllocEvent> mAllocEvents;
CompactTraceTable mTraceTable;
};
} // namespace mozilla
#endif // memory_profiler_NativeProfilerImpl_h

View file

@ -1,121 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=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 "UncensoredAllocator.h"
#include "mozilla/Assertions.h"
#include "mozilla/Unused.h"
#include "MainThreadUtils.h"
#include "jsfriendapi.h"
#include "nsDebug.h"
#include "prlock.h"
#ifdef MOZ_REPLACE_MALLOC
#include "replace_malloc_bridge.h"
#endif
namespace mozilla {
#ifdef MOZ_REPLACE_MALLOC
MOZ_THREAD_LOCAL(bool) MallocHook::mEnabledTLS;
NativeProfiler* MallocHook::mNativeProfiler;
malloc_hook_table_t MallocHook::mMallocHook;
#endif
AutoUseUncensoredAllocator::AutoUseUncensoredAllocator()
{
#ifdef MOZ_REPLACE_MALLOC
MallocHook::mEnabledTLS.set(false);
#endif
}
AutoUseUncensoredAllocator::~AutoUseUncensoredAllocator()
{
#ifdef MOZ_REPLACE_MALLOC
MallocHook::mEnabledTLS.set(true);
#endif
}
bool
MallocHook::Enabled()
{
#ifdef MOZ_REPLACE_MALLOC
return mEnabledTLS.get() && mNativeProfiler;
#else
return false;
#endif
}
void*
MallocHook::SampleNative(void* aAddr, size_t aSize)
{
#ifdef MOZ_REPLACE_MALLOC
if (MallocHook::Enabled()) {
mNativeProfiler->sampleNative(aAddr, aSize);
}
#endif
return aAddr;
}
void
MallocHook::RemoveNative(void* aAddr)
{
#ifdef MOZ_REPLACE_MALLOC
if (MallocHook::Enabled()) {
mNativeProfiler->removeNative(aAddr);
}
#endif
}
void
MallocHook::Initialize()
{
#ifdef MOZ_REPLACE_MALLOC
MOZ_ASSERT(NS_IsMainThread());
mMallocHook.free_hook = RemoveNative;
mMallocHook.malloc_hook = SampleNative;
ReplaceMallocBridge* bridge = ReplaceMallocBridge::Get(3);
if (bridge) {
mozilla::Unused << bridge->RegisterHook("memory-profiler", nullptr, nullptr);
}
bool success = mEnabledTLS.init();
if (NS_WARN_IF(!success)) {
return;
}
#endif
}
void
MallocHook::Enable(NativeProfiler* aNativeProfiler)
{
#ifdef MOZ_REPLACE_MALLOC
MOZ_ASSERT(NS_IsMainThread());
ReplaceMallocBridge* bridge = ReplaceMallocBridge::Get(3);
if (bridge) {
const malloc_table_t* alloc_funcs =
bridge->RegisterHook("memory-profiler", nullptr, &mMallocHook);
if (alloc_funcs) {
mNativeProfiler = aNativeProfiler;
}
}
#endif
}
void
MallocHook::Disable()
{
#ifdef MOZ_REPLACE_MALLOC
MOZ_ASSERT(NS_IsMainThread());
ReplaceMallocBridge* bridge = ReplaceMallocBridge::Get(3);
if (bridge) {
bridge->RegisterHook("memory-profiler", nullptr, nullptr);
mNativeProfiler = nullptr;
}
#endif
}
} // namespace mozilla

View file

@ -1,48 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=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/. */
#ifndef memory_profiler_UncensoredAllocator_h
#define memory_profiler_UncensoredAllocator_h
#include "mozilla/Attributes.h"
#include "mozilla/ThreadLocal.h"
#ifdef MOZ_REPLACE_MALLOC
#include "replace_malloc_bridge.h"
#endif
class NativeProfiler;
namespace mozilla {
class MallocHook final
{
public:
static void Initialize();
static void Enable(NativeProfiler* aNativeProfiler);
static void Disable();
static bool Enabled();
private:
static void* SampleNative(void* aAddr, size_t aSize);
static void RemoveNative(void* aAddr);
#ifdef MOZ_REPLACE_MALLOC
static MOZ_THREAD_LOCAL(bool) mEnabledTLS;
static NativeProfiler* mNativeProfiler;
static malloc_hook_table_t mMallocHook;
#endif
friend class AutoUseUncensoredAllocator;
};
class MOZ_RAII AutoUseUncensoredAllocator final
{
public:
AutoUseUncensoredAllocator();
~AutoUseUncensoredAllocator();
};
} // namespace mozilla
#endif // memory_profiler_UncensoredAllocator_h

View file

@ -4,26 +4,5 @@
# 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/.
if CONFIG['MOZ_ENABLE_PROFILER_SPS']:
XPIDL_MODULE = 'memory_profiler'
XPIDL_SOURCES += [
'nsIMemoryProfiler.idl',
]
UNIFIED_SOURCES += [
'GCHeapProfilerImpl.cpp',
'MemoryProfiler.cpp',
'NativeProfilerImpl.cpp',
'nsMemoryProfilerFactory.cpp',
'UncensoredAllocator.cpp',
]
LOCAL_INCLUDES += [
'/js/xpconnect/src',
'/xpcom/base',
]
FINAL_LIBRARY = 'xul'
if CONFIG['GNU_CXX']:
CXXFLAGS += ['-Wno-error=shadow']

View file

@ -1,72 +0,0 @@
/* -*- 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"
/**
* The memory profiler samples allocation events. An allocation event
* includes a type (what and at where is going to be allocated), a
* size, a timestamp and the corresponding stack trace. Free events
* are also tracked. For managed languages, namely languages relying
* on garbage collection, a free event is generated when an object is
* reclaimed by the garbage collector. These sampled events can be
* used to approximate the full history of allocations afterwards.
* That means we can get various memory profiles of a program in
* different perspectives by post-processing the history in different
* ways. The profiler is designed at the very beginning to support not
* only JavaScript but also native codes. Naturally, not only
* JavaScript objects but also native allocations are tracked.
*
* The result returned is the sampled allocation event traces in a
* compact format. The events is sorted according to the timestamp
* when the event happened. Each event has a trace index pointing to
* the traces table. Each trace entry has a name index pointing to the
* names table and a parent index pointing to the parent trace in the
* traces table. By following the trace index one could rebuild the
* complete backtrace of an allocation event.
*
* [ Events ]
* +-------+-------+ +-------+
* | Size | Size | | Size |
* |-------|-------| |-------|
* | Time | Time |......| Time |
* |-------|-------| |-------|
* +-- Trace | Trace | | Trace |
* | +-------+-------+ +-------+
* |
* | [ Traces ]
* +->--------+--------+ +--------+ +--------+
* -| Name | Name | | Name | | Name |
* / |--------|--------|...|--------|...|--------|
* | | Parent | Parent | | Parent | | Parent |
* | +---|----+----^--++ +--^--|--+ +---^----+
* | | | | | | |
* | +---------+ +-------+ +----------+
* | [ Names ]
* | +-----------------+-----------------+
* +-> Function name | Function name |
* | & line numbers | & line numbers |......
* +-----------------+-----------------+
*
*/
[scriptable, uuid(1e10e7a9-bc05-4878-a687-36c9ea4428b1)]
interface nsIMemoryProfiler : nsISupports
{
void startProfiler();
void stopProfiler();
void resetProfiler();
/**
* Get results in an object which contains three tables:
* {
* names, // an array of function names and positions
* traces, // an array of {nameIdx, parentIdx}
* events, // an array of {size, timestamp, traceIdx}
* }
* Should only be called after stopProfiler.
*/
[implicit_jscontext]
jsval getResults();
};

View file

@ -1,32 +0,0 @@
/* -*- Mode: C++; tab-width: 20; 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 "mozilla/ModuleUtils.h"
#include "nsCOMPtr.h"
#include "MemoryProfiler.h"
using mozilla::MemoryProfiler;
NS_GENERIC_FACTORY_CONSTRUCTOR(MemoryProfiler)
NS_DEFINE_NAMED_CID(MEMORY_PROFILER_CID);
static const mozilla::Module::CIDEntry kMemoryProfilerCIDs[] = {
{ &kMEMORY_PROFILER_CID, false, nullptr, MemoryProfilerConstructor },
{ nullptr }
};
static const mozilla::Module::ContractIDEntry kMemoryProfilerContracts[] = {
{ MEMORY_PROFILER_CONTRACT_ID, &kMEMORY_PROFILER_CID },
{ nullptr }
};
static const mozilla::Module kMemoryProfilerModule = {
mozilla::Module::kVersion,
kMemoryProfilerCIDs,
kMemoryProfilerContracts
};
NSMODULE_DEFN(nsMemoryProfilerModule) = &kMemoryProfilerModule;

View file

@ -1,678 +0,0 @@
/* -*- 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/. */
/*
* This is an implementation of stack unwinding according to a subset
* of the ARM Exception Handling ABI, as described in:
* http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038a/IHI0038A_ehabi.pdf
*
* This handles only the ARM-defined "personality routines" (chapter
* 9), and don't track the value of FP registers, because profiling
* needs only chain of PC/SP values.
*
* Because the exception handling info may not be accurate for all
* possible places where an async signal could occur (e.g., in a
* prologue or epilogue), this bounds-checks all stack accesses.
*
* This file uses "struct" for structures in the exception tables and
* "class" otherwise. We should avoid violating the C++11
* standard-layout rules in the former.
*/
#include "EHABIStackWalk.h"
#include "shared-libraries.h"
#include "platform.h"
#include "mozilla/Atomics.h"
#include "mozilla/Attributes.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/EndianUtils.h"
#include <algorithm>
#include <elf.h>
#include <stdint.h>
#include <vector>
#include <string>
#ifndef PT_ARM_EXIDX
#define PT_ARM_EXIDX 0x70000001
#endif
// Bug 1082817: ICS B2G has a buggy linker that doesn't always ensure
// that the EXIDX is sorted by address, as the spec requires. So in
// that case we build and sort an array of pointers into the index,
// and binary-search that; otherwise, we search the index in place
// (avoiding the time and space overhead of the indirection).
#if defined(ANDROID_VERSION) && ANDROID_VERSION < 16
#define HAVE_UNSORTED_EXIDX
#endif
namespace mozilla {
struct PRel31 {
uint32_t mBits;
bool topBit() const { return mBits & 0x80000000; }
uint32_t value() const { return mBits & 0x7fffffff; }
int32_t offset() const { return (static_cast<int32_t>(mBits) << 1) >> 1; }
const void *compute() const {
return reinterpret_cast<const char *>(this) + offset();
}
private:
PRel31(const PRel31 &copied) = delete;
PRel31() = delete;
};
struct EHEntry {
PRel31 startPC;
PRel31 exidx;
private:
EHEntry(const EHEntry &copied) = delete;
EHEntry() = delete;
};
class EHState {
// Note that any core register can be used as a "frame pointer" to
// influence the unwinding process, so this must track all of them.
uint32_t mRegs[16];
public:
bool unwind(const EHEntry *aEntry, const void *stackBase);
uint32_t &operator[](int i) { return mRegs[i]; }
const uint32_t &operator[](int i) const { return mRegs[i]; }
EHState(const mcontext_t &);
};
enum {
R_SP = 13,
R_LR = 14,
R_PC = 15
};
#ifdef HAVE_UNSORTED_EXIDX
class EHEntryHandle {
const EHEntry *mValue;
public:
EHEntryHandle(const EHEntry *aEntry) : mValue(aEntry) { }
const EHEntry *value() const { return mValue; }
};
bool operator<(const EHEntryHandle &lhs, const EHEntryHandle &rhs) {
return lhs.value()->startPC.compute() < rhs.value()->startPC.compute();
}
#endif
class EHTable {
uint32_t mStartPC;
uint32_t mEndPC;
uint32_t mLoadOffset;
#ifdef HAVE_UNSORTED_EXIDX
// In principle we should be able to binary-search the index section in
// place, but the ICS toolchain's linker is noncompliant and produces
// indices that aren't entirely sorted (e.g., libc). So we have this:
std::vector<EHEntryHandle> mEntries;
typedef std::vector<EHEntryHandle>::const_iterator EntryIterator;
EntryIterator entriesBegin() const { return mEntries.begin(); }
EntryIterator entriesEnd() const { return mEntries.end(); }
static const EHEntry* entryGet(EntryIterator aEntry) {
return aEntry->value();
}
#else
typedef const EHEntry *EntryIterator;
EntryIterator mEntriesBegin, mEntriesEnd;
EntryIterator entriesBegin() const { return mEntriesBegin; }
EntryIterator entriesEnd() const { return mEntriesEnd; }
static const EHEntry* entryGet(EntryIterator aEntry) { return aEntry; }
#endif
std::string mName;
public:
EHTable(const void *aELF, size_t aSize, const std::string &aName);
const EHEntry *lookup(uint32_t aPC) const;
bool isValid() const { return entriesEnd() != entriesBegin(); }
const std::string &name() const { return mName; }
uint32_t startPC() const { return mStartPC; }
uint32_t endPC() const { return mEndPC; }
uint32_t loadOffset() const { return mLoadOffset; }
};
class EHAddrSpace {
std::vector<uint32_t> mStarts;
std::vector<EHTable> mTables;
static mozilla::Atomic<const EHAddrSpace*> sCurrent;
public:
explicit EHAddrSpace(const std::vector<EHTable>& aTables);
const EHTable *lookup(uint32_t aPC) const;
static void Update();
static const EHAddrSpace *Get();
};
void EHABIStackWalkInit()
{
EHAddrSpace::Update();
}
size_t EHABIStackWalk(const mcontext_t &aContext, void *stackBase,
void **aSPs, void **aPCs, const size_t aNumFrames)
{
const EHAddrSpace *space = EHAddrSpace::Get();
EHState state(aContext);
size_t count = 0;
while (count < aNumFrames) {
uint32_t pc = state[R_PC], sp = state[R_SP];
aPCs[count] = reinterpret_cast<void *>(pc);
aSPs[count] = reinterpret_cast<void *>(sp);
count++;
if (!space)
break;
// TODO: cache these lookups. Binary-searching libxul is
// expensive (possibly more expensive than doing the actual
// unwind), and even a small cache should help.
const EHTable *table = space->lookup(pc);
if (!table)
break;
const EHEntry *entry = table->lookup(pc);
if (!entry)
break;
if (!state.unwind(entry, stackBase))
break;
}
return count;
}
class EHInterp {
public:
// Note that stackLimit is exclusive and stackBase is inclusive
// (i.e, stackLimit < SP <= stackBase), following the convention
// set by the AAPCS spec.
EHInterp(EHState &aState, const EHEntry *aEntry,
uint32_t aStackLimit, uint32_t aStackBase)
: mState(aState),
mStackLimit(aStackLimit),
mStackBase(aStackBase),
mNextWord(0),
mWordsLeft(0),
mFailed(false)
{
const PRel31 &exidx = aEntry->exidx;
uint32_t firstWord;
if (exidx.mBits == 1) { // EXIDX_CANTUNWIND
mFailed = true;
return;
}
if (exidx.topBit()) {
firstWord = exidx.mBits;
} else {
mNextWord = reinterpret_cast<const uint32_t *>(exidx.compute());
firstWord = *mNextWord++;
}
switch (firstWord >> 24) {
case 0x80: // short
mWord = firstWord << 8;
mBytesLeft = 3;
break;
case 0x81: case 0x82: // long; catch descriptor size ignored
mWord = firstWord << 16;
mBytesLeft = 2;
mWordsLeft = (firstWord >> 16) & 0xff;
break;
default:
// unknown personality
mFailed = true;
}
}
bool unwind();
private:
// TODO: GCC has been observed not CSEing repeated reads of
// mState[R_SP] with writes to mFailed between them, suggesting that
// it hasn't determined that they can't alias and is thus missing
// optimization opportunities. So, we may want to flatten EHState
// into this class; this may also make the code simpler.
EHState &mState;
uint32_t mStackLimit;
uint32_t mStackBase;
const uint32_t *mNextWord;
uint32_t mWord;
uint8_t mWordsLeft;
uint8_t mBytesLeft;
bool mFailed;
enum {
I_ADDSP = 0x00, // 0sxxxxxx (subtract if s)
M_ADDSP = 0x80,
I_POPMASK = 0x80, // 1000iiii iiiiiiii (if any i set)
M_POPMASK = 0xf0,
I_MOVSP = 0x90, // 1001nnnn
M_MOVSP = 0xf0,
I_POPN = 0xa0, // 1010lnnn
M_POPN = 0xf0,
I_FINISH = 0xb0, // 10110000
I_POPLO = 0xb1, // 10110001 0000iiii (if any i set)
I_ADDSPBIG = 0xb2, // 10110010 uleb128
I_POPFDX = 0xb3, // 10110011 sssscccc
I_POPFDX8 = 0xb8, // 10111nnn
M_POPFDX8 = 0xf8,
// "Intel Wireless MMX" extensions omitted.
I_POPFDD = 0xc8, // 1100100h sssscccc
M_POPFDD = 0xfe,
I_POPFDD8 = 0xd0, // 11010nnn
M_POPFDD8 = 0xf8
};
uint8_t next() {
if (mBytesLeft == 0) {
if (mWordsLeft == 0) {
return I_FINISH;
}
mWordsLeft--;
mWord = *mNextWord++;
mBytesLeft = 4;
}
mBytesLeft--;
mWord = (mWord << 8) | (mWord >> 24); // rotate
return mWord;
}
uint32_t &vSP() { return mState[R_SP]; }
uint32_t *ptrSP() { return reinterpret_cast<uint32_t *>(vSP()); }
void checkStackBase() { if (vSP() > mStackBase) mFailed = true; }
void checkStackLimit() { if (vSP() <= mStackLimit) mFailed = true; }
void checkStackAlign() { if ((vSP() & 3) != 0) mFailed = true; }
void checkStack() {
checkStackBase();
checkStackLimit();
checkStackAlign();
}
void popRange(uint8_t first, uint8_t last, uint16_t mask) {
bool hasSP = false;
uint32_t tmpSP;
if (mask == 0)
mFailed = true;
for (uint8_t r = first; r <= last; ++r) {
if (mask & 1) {
if (r == R_SP) {
hasSP = true;
tmpSP = *ptrSP();
} else
mState[r] = *ptrSP();
vSP() += 4;
checkStackBase();
if (mFailed)
return;
}
mask >>= 1;
}
if (hasSP) {
vSP() = tmpSP;
checkStack();
}
}
};
bool EHState::unwind(const EHEntry *aEntry, const void *stackBasePtr) {
// The unwinding program cannot set SP to less than the initial value.
uint32_t stackLimit = mRegs[R_SP] - 4;
uint32_t stackBase = reinterpret_cast<uint32_t>(stackBasePtr);
EHInterp interp(*this, aEntry, stackLimit, stackBase);
return interp.unwind();
}
bool EHInterp::unwind() {
mState[R_PC] = 0;
checkStack();
while (!mFailed) {
uint8_t insn = next();
#if DEBUG_EHABI_UNWIND
LOGF("unwind insn = %02x", (unsigned)insn);
#endif
// Try to put the common cases first.
// 00xxxxxx: vsp = vsp + (xxxxxx << 2) + 4
// 01xxxxxx: vsp = vsp - (xxxxxx << 2) - 4
if ((insn & M_ADDSP) == I_ADDSP) {
uint32_t offset = ((insn & 0x3f) << 2) + 4;
if (insn & 0x40) {
vSP() -= offset;
checkStackLimit();
} else {
vSP() += offset;
checkStackBase();
}
continue;
}
// 10100nnn: Pop r4-r[4+nnn]
// 10101nnn: Pop r4-r[4+nnn], r14
if ((insn & M_POPN) == I_POPN) {
uint8_t n = (insn & 0x07) + 1;
bool lr = insn & 0x08;
uint32_t *ptr = ptrSP();
vSP() += (n + (lr ? 1 : 0)) * 4;
checkStackBase();
for (uint8_t r = 4; r < 4 + n; ++r)
mState[r] = *ptr++;
if (lr)
mState[R_LR] = *ptr++;
continue;
}
// 1011000: Finish
if (insn == I_FINISH) {
if (mState[R_PC] == 0) {
mState[R_PC] = mState[R_LR];
// Non-standard change (bug 916106): Prevent the caller from
// re-using LR. Since the caller is by definition not a leaf
// routine, it will have to restore LR from somewhere to
// return to its own caller, so we can safely zero it here.
// This makes a difference only if an error in unwinding
// (e.g., caused by starting from within a prologue/epilogue)
// causes us to load a pointer to a leaf routine as LR; if we
// don't do something, we'll go into an infinite loop of
// "returning" to that same function.
mState[R_LR] = 0;
}
return true;
}
// 1001nnnn: Set vsp = r[nnnn]
if ((insn & M_MOVSP) == I_MOVSP) {
vSP() = mState[insn & 0x0f];
checkStack();
continue;
}
// 11001000 sssscccc: Pop VFP regs D[16+ssss]-D[16+ssss+cccc] (as FLDMFDD)
// 11001001 sssscccc: Pop VFP regs D[ssss]-D[ssss+cccc] (as FLDMFDD)
if ((insn & M_POPFDD) == I_POPFDD) {
uint8_t n = (next() & 0x0f) + 1;
// Note: if the 16+ssss+cccc > 31, the encoding is reserved.
// As the space is currently unused, we don't try to check.
vSP() += 8 * n;
checkStackBase();
continue;
}
// 11010nnn: Pop VFP regs D[8]-D[8+nnn] (as FLDMFDD)
if ((insn & M_POPFDD8) == I_POPFDD8) {
uint8_t n = (insn & 0x07) + 1;
vSP() += 8 * n;
checkStackBase();
continue;
}
// 10110010 uleb128: vsp = vsp + 0x204 + (uleb128 << 2)
if (insn == I_ADDSPBIG) {
uint32_t acc = 0;
uint8_t shift = 0;
uint8_t byte;
do {
if (shift >= 32)
return false;
byte = next();
acc |= (byte & 0x7f) << shift;
shift += 7;
} while (byte & 0x80);
uint32_t offset = 0x204 + (acc << 2);
// The calculations above could have overflowed.
// But the one we care about is this:
if (vSP() + offset < vSP())
mFailed = true;
vSP() += offset;
// ...so that this is the only other check needed:
checkStackBase();
continue;
}
// 1000iiii iiiiiiii (i not all 0): Pop under masks {r15-r12}, {r11-r4}
if ((insn & M_POPMASK) == I_POPMASK) {
popRange(4, 15, ((insn & 0x0f) << 8) | next());
continue;
}
// 1011001 0000iiii (i not all 0): Pop under mask {r3-r0}
if (insn == I_POPLO) {
popRange(0, 3, next() & 0x0f);
continue;
}
// 10110011 sssscccc: Pop VFP regs D[ssss]-D[ssss+cccc] (as FLDMFDX)
if (insn == I_POPFDX) {
uint8_t n = (next() & 0x0f) + 1;
vSP() += 8 * n + 4;
checkStackBase();
continue;
}
// 10111nnn: Pop VFP regs D[8]-D[8+nnn] (as FLDMFDX)
if ((insn & M_POPFDX8) == I_POPFDX8) {
uint8_t n = (insn & 0x07) + 1;
vSP() += 8 * n + 4;
checkStackBase();
continue;
}
// unhandled instruction
#ifdef DEBUG_EHABI_UNWIND
LOGF("Unhandled EHABI instruction 0x%02x", insn);
#endif
mFailed = true;
}
return false;
}
bool operator<(const EHTable &lhs, const EHTable &rhs) {
return lhs.startPC() < rhs.startPC();
}
// Async signal unsafe.
EHAddrSpace::EHAddrSpace(const std::vector<EHTable>& aTables)
: mTables(aTables)
{
std::sort(mTables.begin(), mTables.end());
DebugOnly<uint32_t> lastEnd = 0;
for (std::vector<EHTable>::iterator i = mTables.begin();
i != mTables.end(); ++i) {
MOZ_ASSERT(i->startPC() >= lastEnd);
mStarts.push_back(i->startPC());
lastEnd = i->endPC();
}
}
const EHTable *EHAddrSpace::lookup(uint32_t aPC) const {
ptrdiff_t i = (std::upper_bound(mStarts.begin(), mStarts.end(), aPC)
- mStarts.begin()) - 1;
if (i < 0 || aPC >= mTables[i].endPC())
return 0;
return &mTables[i];
}
const EHEntry *EHTable::lookup(uint32_t aPC) const {
MOZ_ASSERT(aPC >= mStartPC);
if (aPC >= mEndPC)
return nullptr;
EntryIterator begin = entriesBegin();
EntryIterator end = entriesEnd();
MOZ_ASSERT(begin < end);
if (aPC < reinterpret_cast<uint32_t>(entryGet(begin)->startPC.compute()))
return nullptr;
while (end - begin > 1) {
#ifdef EHABI_UNWIND_MORE_ASSERTS
if (entryGet(end - 1)->startPC.compute()
< entryGet(begin)->startPC.compute()) {
MOZ_CRASH("unsorted exidx");
}
#endif
EntryIterator mid = begin + (end - begin) / 2;
if (aPC < reinterpret_cast<uint32_t>(entryGet(mid)->startPC.compute()))
end = mid;
else
begin = mid;
}
return entryGet(begin);
}
#if MOZ_LITTLE_ENDIAN
static const unsigned char hostEndian = ELFDATA2LSB;
#elif MOZ_BIG_ENDIAN
static const unsigned char hostEndian = ELFDATA2MSB;
#else
#error "No endian?"
#endif
// Async signal unsafe: std::vector::reserve, std::string copy ctor.
EHTable::EHTable(const void *aELF, size_t aSize, const std::string &aName)
: mStartPC(~0), // largest uint32_t
mEndPC(0),
#ifndef HAVE_UNSORTED_EXIDX
mEntriesBegin(nullptr),
mEntriesEnd(nullptr),
#endif
mName(aName)
{
const uint32_t base = reinterpret_cast<uint32_t>(aELF);
if (aSize < sizeof(Elf32_Ehdr))
return;
const Elf32_Ehdr &file = *(reinterpret_cast<Elf32_Ehdr *>(base));
if (memcmp(&file.e_ident[EI_MAG0], ELFMAG, SELFMAG) != 0 ||
file.e_ident[EI_CLASS] != ELFCLASS32 ||
file.e_ident[EI_DATA] != hostEndian ||
file.e_ident[EI_VERSION] != EV_CURRENT ||
file.e_ident[EI_OSABI] != ELFOSABI_SYSV ||
#ifdef EI_ABIVERSION
file.e_ident[EI_ABIVERSION] != 0 ||
#endif
file.e_machine != EM_ARM ||
file.e_version != EV_CURRENT)
// e_flags?
return;
MOZ_ASSERT(file.e_phoff + file.e_phnum * file.e_phentsize <= aSize);
const Elf32_Phdr *exidxHdr = 0, *zeroHdr = 0;
for (unsigned i = 0; i < file.e_phnum; ++i) {
const Elf32_Phdr &phdr =
*(reinterpret_cast<Elf32_Phdr *>(base + file.e_phoff
+ i * file.e_phentsize));
if (phdr.p_type == PT_ARM_EXIDX) {
exidxHdr = &phdr;
} else if (phdr.p_type == PT_LOAD) {
if (phdr.p_offset == 0) {
zeroHdr = &phdr;
}
if (phdr.p_flags & PF_X) {
mStartPC = std::min(mStartPC, phdr.p_vaddr);
mEndPC = std::max(mEndPC, phdr.p_vaddr + phdr.p_memsz);
}
}
}
if (!exidxHdr)
return;
if (!zeroHdr)
return;
mLoadOffset = base - zeroHdr->p_vaddr;
mStartPC += mLoadOffset;
mEndPC += mLoadOffset;
// Create a sorted index of the index to work around linker bugs.
const EHEntry *startTable =
reinterpret_cast<const EHEntry *>(mLoadOffset + exidxHdr->p_vaddr);
const EHEntry *endTable =
reinterpret_cast<const EHEntry *>(mLoadOffset + exidxHdr->p_vaddr
+ exidxHdr->p_memsz);
#ifdef HAVE_UNSORTED_EXIDX
mEntries.reserve(endTable - startTable);
for (const EHEntry *i = startTable; i < endTable; ++i)
mEntries.push_back(i);
std::sort(mEntries.begin(), mEntries.end());
#else
mEntriesBegin = startTable;
mEntriesEnd = endTable;
#endif
}
mozilla::Atomic<const EHAddrSpace*> EHAddrSpace::sCurrent(nullptr);
// Async signal safe; can fail if Update() hasn't returned yet.
const EHAddrSpace *EHAddrSpace::Get() {
return sCurrent;
}
// Collect unwinding information from loaded objects. Calls after the
// first have no effect. Async signal unsafe.
void EHAddrSpace::Update() {
const EHAddrSpace *space = sCurrent;
if (space)
return;
SharedLibraryInfo info = SharedLibraryInfo::GetInfoForSelf();
std::vector<EHTable> tables;
for (size_t i = 0; i < info.GetSize(); ++i) {
const SharedLibrary &lib = info.GetEntry(i);
if (lib.GetOffset() != 0)
// TODO: if it has a name, and we haven't seen a mapping of
// offset 0 for that file, try opening it and reading the
// headers instead. The only thing I've seen so far that's
// linked so as to need that treatment is the dynamic linker
// itself.
continue;
EHTable tab(reinterpret_cast<const void *>(lib.GetStart()),
lib.GetEnd() - lib.GetStart(), lib.GetName());
if (tab.isValid())
tables.push_back(tab);
}
space = new EHAddrSpace(tables);
if (!sCurrent.compareExchange(nullptr, space)) {
delete space;
space = sCurrent;
}
}
EHState::EHState(const mcontext_t &context) {
#ifdef linux
mRegs[0] = context.arm_r0;
mRegs[1] = context.arm_r1;
mRegs[2] = context.arm_r2;
mRegs[3] = context.arm_r3;
mRegs[4] = context.arm_r4;
mRegs[5] = context.arm_r5;
mRegs[6] = context.arm_r6;
mRegs[7] = context.arm_r7;
mRegs[8] = context.arm_r8;
mRegs[9] = context.arm_r9;
mRegs[10] = context.arm_r10;
mRegs[11] = context.arm_fp;
mRegs[12] = context.arm_ip;
mRegs[13] = context.arm_sp;
mRegs[14] = context.arm_lr;
mRegs[15] = context.arm_pc;
#else
# error "Unhandled OS for ARM EHABI unwinding"
#endif
}
} // namespace mozilla

View file

@ -1,28 +0,0 @@
/* -*- 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/. */
/*
* This is an implementation of stack unwinding according to a subset
* of the ARM Exception Handling ABI; see the comment at the top of
* the .cpp file for details.
*/
#ifndef mozilla_EHABIStackWalk_h__
#define mozilla_EHABIStackWalk_h__
#include <stddef.h>
#include <ucontext.h>
namespace mozilla {
void EHABIStackWalkInit();
size_t EHABIStackWalk(const mcontext_t &aContext, void *stackBase,
void **aSPs, void **aPCs, size_t aNumFrames);
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,181 +0,0 @@
/* -*- 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/. */
#ifndef GeckoSampler_h
#define GeckoSampler_h
#include "platform.h"
#include "ProfileEntry.h"
#include "mozilla/Vector.h"
#include "ThreadProfile.h"
#include "ThreadInfo.h"
#ifndef SPS_STANDALONE
#include "IntelPowerGadget.h"
#endif
#ifdef MOZ_TASK_TRACER
#include "GeckoTaskTracer.h"
#endif
#include <algorithm>
namespace mozilla {
class ProfileGatherer;
} // namespace mozilla
typedef mozilla::Vector<std::string> ThreadNameFilterList;
typedef mozilla::Vector<std::string> FeatureList;
static bool
threadSelected(ThreadInfo* aInfo, const ThreadNameFilterList &aThreadNameFilters) {
if (aThreadNameFilters.empty()) {
return true;
}
std::string name = aInfo->Name();
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
for (uint32_t i = 0; i < aThreadNameFilters.length(); ++i) {
std::string filter = aThreadNameFilters[i];
std::transform(filter.begin(), filter.end(), filter.begin(), ::tolower);
// Crude, non UTF-8 compatible, case insensitive substring search
if (name.find(filter) != std::string::npos) {
return true;
}
}
return false;
}
extern mozilla::TimeStamp sLastTracerEvent;
extern int sFrameNumber;
extern int sLastFrameNumber;
class GeckoSampler: public Sampler {
public:
GeckoSampler(double aInterval, int aEntrySize,
const char** aFeatures, uint32_t aFeatureCount,
const char** aThreadNameFilters, uint32_t aFilterCount);
~GeckoSampler();
void RegisterThread(ThreadInfo* aInfo) {
if (!aInfo->IsMainThread() && !mProfileThreads) {
return;
}
if (!threadSelected(aInfo, mThreadNameFilters)) {
return;
}
ThreadProfile* profile = new ThreadProfile(aInfo, mBuffer);
aInfo->SetProfile(profile);
}
// Called within a signal. This function must be reentrant
virtual void Tick(TickSample* sample) override;
// Immediately captures the calling thread's call stack and returns it.
virtual SyncProfile* GetBacktrace() override;
// Called within a signal. This function must be reentrant
virtual void RequestSave() override
{
mSaveRequested = true;
#ifdef MOZ_TASK_TRACER
if (mTaskTracer) {
mozilla::tasktracer::StopLogging();
}
#endif
}
virtual void HandleSaveRequest() override;
virtual void DeleteExpiredMarkers() override;
ThreadProfile* GetPrimaryThreadProfile()
{
if (!mPrimaryThreadProfile) {
::MutexAutoLock lock(*sRegisteredThreadsMutex);
for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
ThreadInfo* info = sRegisteredThreads->at(i);
if (info->IsMainThread() && !info->IsPendingDelete()) {
mPrimaryThreadProfile = info->Profile();
break;
}
}
}
return mPrimaryThreadProfile;
}
void ToStreamAsJSON(std::ostream& stream, double aSinceTime = 0);
#ifndef SPS_STANDALONE
virtual JSObject *ToJSObject(JSContext *aCx, double aSinceTime = 0);
void GetGatherer(nsISupports** aRetVal);
#endif
mozilla::UniquePtr<char[]> ToJSON(double aSinceTime = 0);
virtual void ToJSObjectAsync(double aSinceTime = 0, mozilla::dom::Promise* aPromise = 0);
void StreamMetaJSCustomObject(SpliceableJSONWriter& aWriter);
void StreamTaskTracer(SpliceableJSONWriter& aWriter);
void FlushOnJSShutdown(JSContext* aContext);
bool ProfileJS() const { return mProfileJS; }
bool ProfileJava() const { return mProfileJava; }
bool ProfileGPU() const { return mProfileGPU; }
bool ProfilePower() const { return mProfilePower; }
bool ProfileThreads() const override { return mProfileThreads; }
bool InPrivacyMode() const { return mPrivacyMode; }
bool AddMainThreadIO() const { return mAddMainThreadIO; }
bool ProfileMemory() const { return mProfileMemory; }
bool TaskTracer() const { return mTaskTracer; }
bool LayersDump() const { return mLayersDump; }
bool DisplayListDump() const { return mDisplayListDump; }
bool ProfileRestyle() const { return mProfileRestyle; }
const ThreadNameFilterList& ThreadNameFilters() { return mThreadNameFilters; }
const FeatureList& Features() { return mFeatures; }
void GetBufferInfo(uint32_t *aCurrentPosition, uint32_t *aTotalSize, uint32_t *aGeneration);
protected:
// Called within a signal. This function must be reentrant
virtual void InplaceTick(TickSample* sample);
// Not implemented on platforms which do not support backtracing
void doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample);
void StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime);
// This represent the application's main thread (SAMPLER_INIT)
ThreadProfile* mPrimaryThreadProfile;
RefPtr<ProfileBuffer> mBuffer;
bool mSaveRequested;
bool mAddLeafAddresses;
bool mUseStackWalk;
bool mProfileJS;
bool mProfileGPU;
bool mProfileThreads;
bool mProfileJava;
bool mProfilePower;
bool mLayersDump;
bool mDisplayListDump;
bool mProfileRestyle;
// Keep the thread filter to check against new thread that
// are started while profiling
ThreadNameFilterList mThreadNameFilters;
FeatureList mFeatures;
bool mPrivacyMode;
bool mAddMainThreadIO;
bool mProfileMemory;
bool mTaskTracer;
#if defined(XP_WIN)
IntelPowerGadget* mIntelPowerGadget;
#endif
private:
RefPtr<mozilla::ProfileGatherer> mGatherer;
};
#endif

View file

@ -1,310 +0,0 @@
/*
* Copyright 2013, Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Author: Joe Olivas <joseph.k.olivas@intel.com>
*/
#include "nsDebug.h"
#include "nsString.h"
#include "IntelPowerGadget.h"
#include "prenv.h"
IntelPowerGadget::IntelPowerGadget() :
libpowergadget(nullptr),
Initialize(nullptr),
GetNumNodes(nullptr),
GetMsrName(nullptr),
GetMsrFunc(nullptr),
ReadMSR(nullptr),
WriteMSR(nullptr),
GetIAFrequency(nullptr),
GetTDP(nullptr),
GetMaxTemperature(nullptr),
GetThresholds(nullptr),
GetTemperature(nullptr),
ReadSample(nullptr),
GetSysTime(nullptr),
GetRDTSC(nullptr),
GetTimeInterval(nullptr),
GetBaseFrequency(nullptr),
GetPowerData(nullptr),
StartLog(nullptr),
StopLog(nullptr),
GetNumMsrs(nullptr),
packageMSR(-1),
cpuMSR(-1),
freqMSR(-1),
tempMSR(-1)
{
}
bool
IntelPowerGadget::Init()
{
bool success = false;
const char *path = PR_GetEnv("IPG_Dir");
nsCString ipg_library;
if (path && *path) {
ipg_library.Append(path);
ipg_library.Append('/');
ipg_library.AppendLiteral(PG_LIBRARY_NAME);
libpowergadget = PR_LoadLibrary(ipg_library.get());
}
if(libpowergadget) {
Initialize = (IPGInitialize) PR_FindFunctionSymbol(libpowergadget, "IntelEnergyLibInitialize");
GetNumNodes = (IPGGetNumNodes) PR_FindFunctionSymbol(libpowergadget, "GetNumNodes");
GetMsrName = (IPGGetMsrName) PR_FindFunctionSymbol(libpowergadget, "GetMsrName");
GetMsrFunc = (IPGGetMsrFunc) PR_FindFunctionSymbol(libpowergadget, "GetMsrFunc");
ReadMSR = (IPGReadMSR) PR_FindFunctionSymbol(libpowergadget, "ReadMSR");
WriteMSR = (IPGWriteMSR) PR_FindFunctionSymbol(libpowergadget, "WriteMSR");
GetIAFrequency = (IPGGetIAFrequency) PR_FindFunctionSymbol(libpowergadget, "GetIAFrequency");
GetTDP = (IPGGetTDP) PR_FindFunctionSymbol(libpowergadget, "GetTDP");
GetMaxTemperature = (IPGGetMaxTemperature) PR_FindFunctionSymbol(libpowergadget, "GetMaxTemperature");
GetThresholds = (IPGGetThresholds) PR_FindFunctionSymbol(libpowergadget, "GetThresholds");
GetTemperature = (IPGGetTemperature) PR_FindFunctionSymbol(libpowergadget, "GetTemperature");
ReadSample = (IPGReadSample) PR_FindFunctionSymbol(libpowergadget, "ReadSample");
GetSysTime = (IPGGetSysTime) PR_FindFunctionSymbol(libpowergadget, "GetSysTime");
GetRDTSC = (IPGGetRDTSC) PR_FindFunctionSymbol(libpowergadget, "GetRDTSC");
GetTimeInterval = (IPGGetTimeInterval) PR_FindFunctionSymbol(libpowergadget, "GetTimeInterval");
GetBaseFrequency = (IPGGetBaseFrequency) PR_FindFunctionSymbol(libpowergadget, "GetBaseFrequency");
GetPowerData = (IPGGetPowerData) PR_FindFunctionSymbol(libpowergadget, "GetPowerData");
StartLog = (IPGStartLog) PR_FindFunctionSymbol(libpowergadget, "StartLog");
StopLog = (IPGStopLog) PR_FindFunctionSymbol(libpowergadget, "StopLog");
GetNumMsrs = (IPGGetNumMsrs) PR_FindFunctionSymbol(libpowergadget, "GetNumMsrs");
}
if(Initialize) {
Initialize();
int msrCount = GetNumberMsrs();
wchar_t name[1024] = {0};
for(int i = 0; i < msrCount; ++i) {
GetMsrName(i, name);
int func = 0;
GetMsrFunc(i, &func);
// MSR for frequency
if(wcscmp(name, L"CPU Frequency") == 0 && (func == 0)) {
this->freqMSR = i;
}
// MSR for Package
else if(wcscmp(name, L"Processor") == 0 && (func == 1)) {
this->packageMSR = i;
}
// MSR for CPU
else if(wcscmp(name, L"IA") == 0 && (func == 1)) {
this->cpuMSR = i;
}
// MSR for Temperature
else if(wcscmp(name, L"Package") == 0 && (func == 2)) {
this->tempMSR = i;
}
}
// Grab one sample at startup for a diff
TakeSample();
success = true;
}
return success;
}
IntelPowerGadget::~IntelPowerGadget()
{
if(libpowergadget) {
NS_WARNING("Unloading PowerGadget library!\n");
PR_UnloadLibrary(libpowergadget);
libpowergadget = nullptr;
Initialize = nullptr;
GetNumNodes = nullptr;
GetMsrName = nullptr;
GetMsrFunc = nullptr;
ReadMSR = nullptr;
WriteMSR = nullptr;
GetIAFrequency = nullptr;
GetTDP = nullptr;
GetMaxTemperature = nullptr;
GetThresholds = nullptr;
GetTemperature = nullptr;
ReadSample = nullptr;
GetSysTime = nullptr;
GetRDTSC = nullptr;
GetTimeInterval = nullptr;
GetBaseFrequency = nullptr;
GetPowerData = nullptr;
StartLog = nullptr;
StopLog = nullptr;
GetNumMsrs = nullptr;
}
}
int
IntelPowerGadget::GetNumberNodes()
{
int nodes = 0;
if(GetNumNodes) {
int ok = GetNumNodes(&nodes);
}
return nodes;
}
int
IntelPowerGadget::GetNumberMsrs()
{
int msrs = 0;
if(GetNumMsrs) {
int ok = GetNumMsrs(&msrs);
}
return msrs;
}
int
IntelPowerGadget::GetCPUFrequency(int node)
{
int frequency = 0;
if(GetIAFrequency) {
int ok = GetIAFrequency(node, &frequency);
}
return frequency;
}
double
IntelPowerGadget::GetTdp(int node)
{
double tdp = 0.0;
if(GetTDP) {
int ok = GetTDP(node, &tdp);
}
return tdp;
}
int
IntelPowerGadget::GetMaxTemp(int node)
{
int maxTemperatureC = 0;
if(GetMaxTemperature) {
int ok = GetMaxTemperature(node, &maxTemperatureC);
}
return maxTemperatureC;
}
int
IntelPowerGadget::GetTemp(int node)
{
int temperatureC = 0;
if(GetTemperature) {
int ok = GetTemperature(node, &temperatureC);
}
return temperatureC;
}
int
IntelPowerGadget::TakeSample()
{
int ok = 0;
if(ReadSample) {
ok = ReadSample();
}
return ok;
}
uint64_t
IntelPowerGadget::GetRdtsc()
{
uint64_t rdtsc = 0;
if(GetRDTSC) {
int ok = GetRDTSC(&rdtsc);
}
return rdtsc;
}
double
IntelPowerGadget::GetInterval()
{
double interval = 0.0;
if(GetTimeInterval) {
int ok = GetTimeInterval(&interval);
}
return interval;
}
double
IntelPowerGadget::GetCPUBaseFrequency(int node)
{
double freq = 0.0;
if(GetBaseFrequency) {
int ok = GetBaseFrequency(node, &freq);
}
return freq;
}
double
IntelPowerGadget::GetTotalPackagePowerInWatts()
{
int nodes = GetNumberNodes();
double totalPower = 0.0;
for(int i = 0; i < nodes; ++i) {
totalPower += GetPackagePowerInWatts(i);
}
return totalPower;
}
double
IntelPowerGadget::GetPackagePowerInWatts(int node)
{
int numResult = 0;
double result[] = {0.0, 0.0, 0.0};
if(GetPowerData && packageMSR != -1) {
int ok = GetPowerData(node, packageMSR, result, &numResult);
}
return result[0];
}
double
IntelPowerGadget::GetTotalCPUPowerInWatts()
{
int nodes = GetNumberNodes();
double totalPower = 0.0;
for(int i = 0; i < nodes; ++i) {
totalPower += GetCPUPowerInWatts(i);
}
return totalPower;
}
double
IntelPowerGadget::GetCPUPowerInWatts(int node)
{
int numResult = 0;
double result[] = {0.0, 0.0, 0.0};
if(GetPowerData && cpuMSR != -1) {
int ok = GetPowerData(node, cpuMSR, result, &numResult);
}
return result[0];
}
double
IntelPowerGadget::GetTotalGPUPowerInWatts()
{
int nodes = GetNumberNodes();
double totalPower = 0.0;
for(int i = 0; i < nodes; ++i) {
totalPower += GetGPUPowerInWatts(i);
}
return totalPower;
}
double
IntelPowerGadget::GetGPUPowerInWatts(int node)
{
return 0.0;
}

View file

@ -1,150 +0,0 @@
/*
* Copyright 2013, Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Author: Joe Olivas <joseph.k.olivas@intel.com>
*/
#ifndef profiler_IntelPowerGadget_h
#define profiler_IntelPowerGadget_h
#ifdef _MSC_VER
typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
#include <stdint.h>
#endif
#include "prlink.h"
typedef int (*IPGInitialize) ();
typedef int (*IPGGetNumNodes) (int *nNodes);
typedef int (*IPGGetNumMsrs) (int *nMsr);
typedef int (*IPGGetMsrName) (int iMsr, wchar_t *szName);
typedef int (*IPGGetMsrFunc) (int iMsr, int *pFuncID);
typedef int (*IPGReadMSR) (int iNode, unsigned int address, uint64_t *value);
typedef int (*IPGWriteMSR) (int iNode, unsigned int address, uint64_t value);
typedef int (*IPGGetIAFrequency) (int iNode, int *freqInMHz);
typedef int (*IPGGetTDP) (int iNode, double *TDP);
typedef int (*IPGGetMaxTemperature) (int iNode, int *degreeC);
typedef int (*IPGGetThresholds) (int iNode, int *degree1C, int *degree2C);
typedef int (*IPGGetTemperature) (int iNode, int *degreeC);
typedef int (*IPGReadSample) ();
typedef int (*IPGGetSysTime) (void *pSysTime);
typedef int (*IPGGetRDTSC) (uint64_t *pTSC);
typedef int (*IPGGetTimeInterval) (double *pOffset);
typedef int (*IPGGetBaseFrequency) (int iNode, double *pBaseFrequency);
typedef int (*IPGGetPowerData) (int iNode, int iMSR, double *pResult, int *nResult);
typedef int (*IPGStartLog) (wchar_t *szFileName);
typedef int (*IPGStopLog) ();
#if defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64)
#define PG_LIBRARY_NAME "EnergyLib64"
#else
#define PG_LIBRARY_NAME "EnergyLib32"
#endif
class IntelPowerGadget
{
public:
IntelPowerGadget();
~IntelPowerGadget();
// Fails if initialization is incomplete
bool Init();
// Returns the number of packages on the system
int GetNumberNodes();
// Returns the number of MSRs being tracked
int GetNumberMsrs();
// Given a node, returns the temperature
int GetCPUFrequency(int);
// Returns the TDP of the given node
double GetTdp(int);
// Returns the maximum temperature for the given node
int GetMaxTemp(int);
// Returns the current temperature in degrees C
// of the given node
int GetTemp(int);
// Takes a sample of data. Must be called before
// any current data is retrieved.
int TakeSample();
// Gets the timestamp of the most recent sample
uint64_t GetRdtsc();
// returns number of seconds between the last
// two samples
double GetInterval();
// Returns the base frequency for the given node
double GetCPUBaseFrequency(int node);
// Returns the combined package power for all
// packages on the system for the last sample.
double GetTotalPackagePowerInWatts();
double GetPackagePowerInWatts(int node);
// Returns the combined CPU power for all
// packages on the system for the last sample.
// If the reading is not available, returns 0.0
double GetTotalCPUPowerInWatts();
double GetCPUPowerInWatts(int node);
// Returns the combined GPU power for all
// packages on the system for the last sample.
// If the reading is not available, returns 0.0
double GetTotalGPUPowerInWatts();
double GetGPUPowerInWatts(int node);
private:
PRLibrary *libpowergadget;
IPGInitialize Initialize;
IPGGetNumNodes GetNumNodes;
IPGGetNumMsrs GetNumMsrs;
IPGGetMsrName GetMsrName;
IPGGetMsrFunc GetMsrFunc;
IPGReadMSR ReadMSR;
IPGWriteMSR WriteMSR;
IPGGetIAFrequency GetIAFrequency;
IPGGetTDP GetTDP;
IPGGetMaxTemperature GetMaxTemperature;
IPGGetThresholds GetThresholds;
IPGGetTemperature GetTemperature;
IPGReadSample ReadSample;
IPGGetSysTime GetSysTime;
IPGGetRDTSC GetRDTSC;
IPGGetTimeInterval GetTimeInterval;
IPGGetBaseFrequency GetBaseFrequency;
IPGGetPowerData GetPowerData;
IPGStartLog StartLog;
IPGStopLog StopLog;
int packageMSR;
int cpuMSR;
int freqMSR;
int tempMSR;
};
#endif // profiler_IntelPowerGadget_h

View file

@ -1,89 +0,0 @@
/* -*- 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 "ProfileBuffer.h"
ProfileBuffer::ProfileBuffer(int aEntrySize)
: mEntries(MakeUnique<ProfileEntry[]>(aEntrySize))
, mWritePos(0)
, mReadPos(0)
, mEntrySize(aEntrySize)
, mGeneration(0)
{
}
ProfileBuffer::~ProfileBuffer()
{
while (mStoredMarkers.peek()) {
delete mStoredMarkers.popHead();
}
}
// Called from signal, call only reentrant functions
void ProfileBuffer::addTag(const ProfileEntry& aTag)
{
mEntries[mWritePos++] = aTag;
if (mWritePos == mEntrySize) {
// Wrapping around may result in things referenced in the buffer (e.g.,
// JIT code addresses and markers) being incorrectly collected.
MOZ_ASSERT(mGeneration != UINT32_MAX);
mGeneration++;
mWritePos = 0;
}
if (mWritePos == mReadPos) {
// Keep one slot open.
mEntries[mReadPos] = ProfileEntry();
mReadPos = (mReadPos + 1) % mEntrySize;
}
}
void ProfileBuffer::addStoredMarker(ProfilerMarker *aStoredMarker) {
aStoredMarker->SetGeneration(mGeneration);
mStoredMarkers.insert(aStoredMarker);
}
void ProfileBuffer::deleteExpiredStoredMarkers() {
// Delete markers of samples that have been overwritten due to circular
// buffer wraparound.
uint32_t generation = mGeneration;
while (mStoredMarkers.peek() &&
mStoredMarkers.peek()->HasExpired(generation)) {
delete mStoredMarkers.popHead();
}
}
void ProfileBuffer::reset() {
mGeneration += 2;
mReadPos = mWritePos = 0;
}
#define DYNAMIC_MAX_STRING 8192
char* ProfileBuffer::processDynamicTag(int readPos,
int* tagsConsumed, char* tagBuff)
{
int readAheadPos = (readPos + 1) % mEntrySize;
int tagBuffPos = 0;
// Read the string stored in mTagData until the null character is seen
bool seenNullByte = false;
while (readAheadPos != mWritePos && !seenNullByte) {
(*tagsConsumed)++;
ProfileEntry readAheadEntry = mEntries[readAheadPos];
for (size_t pos = 0; pos < sizeof(void*); pos++) {
tagBuff[tagBuffPos] = readAheadEntry.mTagChars[pos];
if (tagBuff[tagBuffPos] == '\0' || tagBuffPos == DYNAMIC_MAX_STRING-2) {
seenNullByte = true;
break;
}
tagBuffPos++;
}
if (!seenNullByte)
readAheadPos = (readAheadPos + 1) % mEntrySize;
}
return tagBuff;
}

View file

@ -1,61 +0,0 @@
/* -*- 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/. */
#ifndef MOZ_PROFILE_BUFFER_H
#define MOZ_PROFILE_BUFFER_H
#include "ProfileEntry.h"
#include "platform.h"
#include "ProfileJSONWriter.h"
#include "mozilla/RefPtr.h"
#include "mozilla/RefCounted.h"
class ProfileBuffer : public mozilla::RefCounted<ProfileBuffer> {
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ProfileBuffer)
explicit ProfileBuffer(int aEntrySize);
virtual ~ProfileBuffer();
void addTag(const ProfileEntry& aTag);
void StreamSamplesToJSON(SpliceableJSONWriter& aWriter, int aThreadId, double aSinceTime,
JSContext* cx, UniqueStacks& aUniqueStacks);
void StreamMarkersToJSON(SpliceableJSONWriter& aWriter, int aThreadId, double aSinceTime,
UniqueStacks& aUniqueStacks);
void DuplicateLastSample(int aThreadId);
void addStoredMarker(ProfilerMarker* aStoredMarker);
// The following two methods are not signal safe! They delete markers.
void deleteExpiredStoredMarkers();
void reset();
protected:
char* processDynamicTag(int readPos, int* tagsConsumed, char* tagBuff);
int FindLastSampleOfThread(int aThreadId);
public:
// Circular buffer 'Keep One Slot Open' implementation for simplicity
mozilla::UniquePtr<ProfileEntry[]> mEntries;
// Points to the next entry we will write to, which is also the one at which
// we need to stop reading.
int mWritePos;
// Points to the entry at which we can start reading.
int mReadPos;
// The number of entries in our buffer.
int mEntrySize;
// How many times mWritePos has wrapped around.
uint32_t mGeneration;
// Markers that marker entries in the buffer might refer to.
ProfilerMarkerLinkedList mStoredMarkers;
};
#endif

View file

@ -1,881 +0,0 @@
/* -*- 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 <ostream>
#include "platform.h"
#include "mozilla/HashFunctions.h"
#ifndef SPS_STANDALONE
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
// JS
#include "jsapi.h"
#include "jsfriendapi.h"
#include "js/TrackedOptimizationInfo.h"
#endif
// Self
#include "ProfileEntry.h"
using mozilla::MakeUnique;
using mozilla::UniquePtr;
using mozilla::Maybe;
using mozilla::Some;
using mozilla::Nothing;
using mozilla::JSONWriter;
////////////////////////////////////////////////////////////////////////
// BEGIN ProfileEntry
ProfileEntry::ProfileEntry()
: mTagData(nullptr)
, mTagName(0)
{ }
// aTagData must not need release (i.e. be a string from the text segment)
ProfileEntry::ProfileEntry(char aTagName, const char *aTagData)
: mTagData(aTagData)
, mTagName(aTagName)
{ }
ProfileEntry::ProfileEntry(char aTagName, ProfilerMarker *aTagMarker)
: mTagMarker(aTagMarker)
, mTagName(aTagName)
{ }
ProfileEntry::ProfileEntry(char aTagName, void *aTagPtr)
: mTagPtr(aTagPtr)
, mTagName(aTagName)
{ }
ProfileEntry::ProfileEntry(char aTagName, double aTagDouble)
: mTagDouble(aTagDouble)
, mTagName(aTagName)
{ }
ProfileEntry::ProfileEntry(char aTagName, uintptr_t aTagOffset)
: mTagOffset(aTagOffset)
, mTagName(aTagName)
{ }
ProfileEntry::ProfileEntry(char aTagName, Address aTagAddress)
: mTagAddress(aTagAddress)
, mTagName(aTagName)
{ }
ProfileEntry::ProfileEntry(char aTagName, int aTagInt)
: mTagInt(aTagInt)
, mTagName(aTagName)
{ }
ProfileEntry::ProfileEntry(char aTagName, char aTagChar)
: mTagChar(aTagChar)
, mTagName(aTagName)
{ }
bool ProfileEntry::is_ent_hint(char hintChar) {
return mTagName == 'h' && mTagChar == hintChar;
}
bool ProfileEntry::is_ent_hint() {
return mTagName == 'h';
}
bool ProfileEntry::is_ent(char tagChar) {
return mTagName == tagChar;
}
void* ProfileEntry::get_tagPtr() {
// No consistency checking. Oh well.
return mTagPtr;
}
// END ProfileEntry
////////////////////////////////////////////////////////////////////////
class JSONSchemaWriter
{
JSONWriter& mWriter;
uint32_t mIndex;
public:
explicit JSONSchemaWriter(JSONWriter& aWriter)
: mWriter(aWriter)
, mIndex(0)
{
aWriter.StartObjectProperty("schema");
}
void WriteField(const char* aName) {
mWriter.IntProperty(aName, mIndex++);
}
~JSONSchemaWriter() {
mWriter.EndObject();
}
};
#ifndef SPS_STANDALONE
class StreamOptimizationTypeInfoOp : public JS::ForEachTrackedOptimizationTypeInfoOp
{
JSONWriter& mWriter;
UniqueJSONStrings& mUniqueStrings;
bool mStartedTypeList;
public:
StreamOptimizationTypeInfoOp(JSONWriter& aWriter, UniqueJSONStrings& aUniqueStrings)
: mWriter(aWriter)
, mUniqueStrings(aUniqueStrings)
, mStartedTypeList(false)
{ }
void readType(const char* keyedBy, const char* name,
const char* location, Maybe<unsigned> lineno) override {
if (!mStartedTypeList) {
mStartedTypeList = true;
mWriter.StartObjectElement();
mWriter.StartArrayProperty("typeset");
}
mWriter.StartObjectElement();
{
mUniqueStrings.WriteProperty(mWriter, "keyedBy", keyedBy);
if (name) {
mUniqueStrings.WriteProperty(mWriter, "name", name);
}
if (location) {
mUniqueStrings.WriteProperty(mWriter, "location", location);
}
if (lineno.isSome()) {
mWriter.IntProperty("line", *lineno);
}
}
mWriter.EndObject();
}
void operator()(JS::TrackedTypeSite site, const char* mirType) override {
if (mStartedTypeList) {
mWriter.EndArray();
mStartedTypeList = false;
} else {
mWriter.StartObjectElement();
}
{
mUniqueStrings.WriteProperty(mWriter, "site", JS::TrackedTypeSiteString(site));
mUniqueStrings.WriteProperty(mWriter, "mirType", mirType);
}
mWriter.EndObject();
}
};
// As mentioned in ProfileEntry.h, the JSON format contains many arrays whose
// elements are laid out according to various schemas to help
// de-duplication. This RAII class helps write these arrays by keeping track of
// the last non-null element written and adding the appropriate number of null
// elements when writing new non-null elements. It also automatically opens and
// closes an array element on the given JSON writer.
//
// Example usage:
//
// // Define the schema of elements in this type of array: [FOO, BAR, BAZ]
// enum Schema : uint32_t {
// FOO = 0,
// BAR = 1,
// BAZ = 2
// };
//
// AutoArraySchemaWriter writer(someJsonWriter, someUniqueStrings);
// if (shouldWriteFoo) {
// writer.IntElement(FOO, getFoo());
// }
// ... etc ...
class MOZ_RAII AutoArraySchemaWriter
{
friend class AutoObjectWriter;
SpliceableJSONWriter& mJSONWriter;
UniqueJSONStrings* mStrings;
uint32_t mNextFreeIndex;
public:
AutoArraySchemaWriter(SpliceableJSONWriter& aWriter, UniqueJSONStrings& aStrings)
: mJSONWriter(aWriter)
, mStrings(&aStrings)
, mNextFreeIndex(0)
{
mJSONWriter.StartArrayElement();
}
// If you don't have access to a UniqueStrings, you had better not try and
// write a string element down the line!
explicit AutoArraySchemaWriter(SpliceableJSONWriter& aWriter)
: mJSONWriter(aWriter)
, mStrings(nullptr)
, mNextFreeIndex(0)
{
mJSONWriter.StartArrayElement();
}
~AutoArraySchemaWriter() {
mJSONWriter.EndArray();
}
void FillUpTo(uint32_t aIndex) {
MOZ_ASSERT(aIndex >= mNextFreeIndex);
mJSONWriter.NullElements(aIndex - mNextFreeIndex);
mNextFreeIndex = aIndex + 1;
}
void IntElement(uint32_t aIndex, uint32_t aValue) {
FillUpTo(aIndex);
mJSONWriter.IntElement(aValue);
}
void DoubleElement(uint32_t aIndex, double aValue) {
FillUpTo(aIndex);
mJSONWriter.DoubleElement(aValue);
}
void StringElement(uint32_t aIndex, const char* aValue) {
MOZ_RELEASE_ASSERT(mStrings);
FillUpTo(aIndex);
mStrings->WriteElement(mJSONWriter, aValue);
}
};
class StreamOptimizationAttemptsOp : public JS::ForEachTrackedOptimizationAttemptOp
{
SpliceableJSONWriter& mWriter;
UniqueJSONStrings& mUniqueStrings;
public:
StreamOptimizationAttemptsOp(SpliceableJSONWriter& aWriter, UniqueJSONStrings& aUniqueStrings)
: mWriter(aWriter),
mUniqueStrings(aUniqueStrings)
{ }
void operator()(JS::TrackedStrategy strategy, JS::TrackedOutcome outcome) override {
enum Schema : uint32_t {
STRATEGY = 0,
OUTCOME = 1
};
AutoArraySchemaWriter writer(mWriter, mUniqueStrings);
writer.StringElement(STRATEGY, JS::TrackedStrategyString(strategy));
writer.StringElement(OUTCOME, JS::TrackedOutcomeString(outcome));
}
};
class StreamJSFramesOp : public JS::ForEachProfiledFrameOp
{
void* mReturnAddress;
UniqueStacks::Stack& mStack;
unsigned mDepth;
public:
StreamJSFramesOp(void* aReturnAddr, UniqueStacks::Stack& aStack)
: mReturnAddress(aReturnAddr)
, mStack(aStack)
, mDepth(0)
{ }
unsigned depth() const {
MOZ_ASSERT(mDepth > 0);
return mDepth;
}
void operator()(const JS::ForEachProfiledFrameOp::FrameHandle& aFrameHandle) override {
UniqueStacks::OnStackFrameKey frameKey(mReturnAddress, mDepth, aFrameHandle);
mStack.AppendFrame(frameKey);
mDepth++;
}
};
#endif
uint32_t UniqueJSONStrings::GetOrAddIndex(const char* aStr)
{
uint32_t index;
StringKey key(aStr);
auto it = mStringToIndexMap.find(key);
if (it != mStringToIndexMap.end()) {
return it->second;
}
index = mStringToIndexMap.size();
mStringToIndexMap[key] = index;
mStringTableWriter.StringElement(aStr);
return index;
}
bool UniqueStacks::FrameKey::operator==(const FrameKey& aOther) const
{
return mLocation == aOther.mLocation &&
mLine == aOther.mLine &&
mCategory == aOther.mCategory &&
mJITAddress == aOther.mJITAddress &&
mJITDepth == aOther.mJITDepth;
}
bool UniqueStacks::StackKey::operator==(const StackKey& aOther) const
{
MOZ_ASSERT_IF(mPrefix == aOther.mPrefix, mPrefixHash == aOther.mPrefixHash);
return mPrefix == aOther.mPrefix && mFrame == aOther.mFrame;
}
UniqueStacks::Stack::Stack(UniqueStacks& aUniqueStacks, const OnStackFrameKey& aRoot)
: mUniqueStacks(aUniqueStacks)
, mStack(aUniqueStacks.GetOrAddFrameIndex(aRoot))
{
}
void UniqueStacks::Stack::AppendFrame(const OnStackFrameKey& aFrame)
{
// Compute the prefix hash and index before mutating mStack.
uint32_t prefixHash = mStack.Hash();
uint32_t prefix = mUniqueStacks.GetOrAddStackIndex(mStack);
mStack.UpdateHash(prefixHash, prefix, mUniqueStacks.GetOrAddFrameIndex(aFrame));
}
uint32_t UniqueStacks::Stack::GetOrAddIndex() const
{
return mUniqueStacks.GetOrAddStackIndex(mStack);
}
uint32_t UniqueStacks::FrameKey::Hash() const
{
uint32_t hash = 0;
if (!mLocation.IsEmpty()) {
#ifdef SPS_STANDALONE
hash = mozilla::HashString(mLocation.c_str());
#else
hash = mozilla::HashString(mLocation.get());
#endif
}
if (mLine.isSome()) {
hash = mozilla::AddToHash(hash, *mLine);
}
if (mCategory.isSome()) {
hash = mozilla::AddToHash(hash, *mCategory);
}
if (mJITAddress.isSome()) {
hash = mozilla::AddToHash(hash, *mJITAddress);
if (mJITDepth.isSome()) {
hash = mozilla::AddToHash(hash, *mJITDepth);
}
}
return hash;
}
uint32_t UniqueStacks::StackKey::Hash() const
{
if (mPrefix.isNothing()) {
return mozilla::HashGeneric(mFrame);
}
return mozilla::AddToHash(*mPrefixHash, mFrame);
}
UniqueStacks::Stack UniqueStacks::BeginStack(const OnStackFrameKey& aRoot)
{
return Stack(*this, aRoot);
}
UniqueStacks::UniqueStacks(JSContext* aContext)
: mContext(aContext)
, mFrameCount(0)
{
mFrameTableWriter.StartBareList();
mStackTableWriter.StartBareList();
}
#ifdef SPS_STANDALONE
uint32_t UniqueStacks::GetOrAddStackIndex(const StackKey& aStack)
{
uint32_t index;
auto it = mStackToIndexMap.find(aStack);
if (it != mStackToIndexMap.end()) {
return it->second;
}
index = mStackToIndexMap.size();
mStackToIndexMap[aStack] = index;
StreamStack(aStack);
return index;
}
#else
uint32_t UniqueStacks::GetOrAddStackIndex(const StackKey& aStack)
{
uint32_t index;
if (mStackToIndexMap.Get(aStack, &index)) {
MOZ_ASSERT(index < mStackToIndexMap.Count());
return index;
}
index = mStackToIndexMap.Count();
mStackToIndexMap.Put(aStack, index);
StreamStack(aStack);
return index;
}
#endif
#ifdef SPS_STANDALONE
uint32_t UniqueStacks::GetOrAddFrameIndex(const OnStackFrameKey& aFrame)
{
uint32_t index;
auto it = mFrameToIndexMap.find(aFrame);
if (it != mFrameToIndexMap.end()) {
MOZ_ASSERT(it->second < mFrameCount);
return it->second;
}
// A manual count is used instead of mFrameToIndexMap.Count() due to
// forwarding of canonical JIT frames above.
index = mFrameCount++;
mFrameToIndexMap[aFrame] = index;
StreamFrame(aFrame);
return index;
}
#else
uint32_t UniqueStacks::GetOrAddFrameIndex(const OnStackFrameKey& aFrame)
{
uint32_t index;
if (mFrameToIndexMap.Get(aFrame, &index)) {
MOZ_ASSERT(index < mFrameCount);
return index;
}
// If aFrame isn't canonical, forward it to the canonical frame's index.
if (aFrame.mJITFrameHandle) {
void* canonicalAddr = aFrame.mJITFrameHandle->canonicalAddress();
if (canonicalAddr != *aFrame.mJITAddress) {
OnStackFrameKey canonicalKey(canonicalAddr, *aFrame.mJITDepth, *aFrame.mJITFrameHandle);
uint32_t canonicalIndex = GetOrAddFrameIndex(canonicalKey);
mFrameToIndexMap.Put(aFrame, canonicalIndex);
return canonicalIndex;
}
}
// A manual count is used instead of mFrameToIndexMap.Count() due to
// forwarding of canonical JIT frames above.
index = mFrameCount++;
mFrameToIndexMap.Put(aFrame, index);
StreamFrame(aFrame);
return index;
}
#endif
uint32_t UniqueStacks::LookupJITFrameDepth(void* aAddr)
{
uint32_t depth;
auto it = mJITFrameDepthMap.find(aAddr);
if (it != mJITFrameDepthMap.end()) {
depth = it->second;
MOZ_ASSERT(depth > 0);
return depth;
}
return 0;
}
void UniqueStacks::AddJITFrameDepth(void* aAddr, unsigned depth)
{
mJITFrameDepthMap[aAddr] = depth;
}
void UniqueStacks::SpliceFrameTableElements(SpliceableJSONWriter& aWriter)
{
mFrameTableWriter.EndBareList();
aWriter.TakeAndSplice(mFrameTableWriter.WriteFunc());
}
void UniqueStacks::SpliceStackTableElements(SpliceableJSONWriter& aWriter)
{
mStackTableWriter.EndBareList();
aWriter.TakeAndSplice(mStackTableWriter.WriteFunc());
}
void UniqueStacks::StreamStack(const StackKey& aStack)
{
enum Schema : uint32_t {
PREFIX = 0,
FRAME = 1
};
AutoArraySchemaWriter writer(mStackTableWriter, mUniqueStrings);
if (aStack.mPrefix.isSome()) {
writer.IntElement(PREFIX, *aStack.mPrefix);
}
writer.IntElement(FRAME, aStack.mFrame);
}
void UniqueStacks::StreamFrame(const OnStackFrameKey& aFrame)
{
enum Schema : uint32_t {
LOCATION = 0,
IMPLEMENTATION = 1,
OPTIMIZATIONS = 2,
LINE = 3,
CATEGORY = 4
};
AutoArraySchemaWriter writer(mFrameTableWriter, mUniqueStrings);
#ifndef SPS_STANDALONE
if (!aFrame.mJITFrameHandle) {
#else
{
#endif
#ifdef SPS_STANDALONE
writer.StringElement(LOCATION, aFrame.mLocation.c_str());
#else
writer.StringElement(LOCATION, aFrame.mLocation.get());
#endif
if (aFrame.mLine.isSome()) {
writer.IntElement(LINE, *aFrame.mLine);
}
if (aFrame.mCategory.isSome()) {
writer.IntElement(CATEGORY, *aFrame.mCategory);
}
}
#ifndef SPS_STANDALONE
else {
const JS::ForEachProfiledFrameOp::FrameHandle& jitFrame = *aFrame.mJITFrameHandle;
writer.StringElement(LOCATION, jitFrame.label());
JS::ProfilingFrameIterator::FrameKind frameKind = jitFrame.frameKind();
MOZ_ASSERT(frameKind == JS::ProfilingFrameIterator::Frame_Ion ||
frameKind == JS::ProfilingFrameIterator::Frame_Baseline);
writer.StringElement(IMPLEMENTATION,
frameKind == JS::ProfilingFrameIterator::Frame_Ion
? "ion"
: "baseline");
if (jitFrame.hasTrackedOptimizations()) {
writer.FillUpTo(OPTIMIZATIONS);
mFrameTableWriter.StartObjectElement();
{
mFrameTableWriter.StartArrayProperty("types");
{
StreamOptimizationTypeInfoOp typeInfoOp(mFrameTableWriter, mUniqueStrings);
jitFrame.forEachOptimizationTypeInfo(typeInfoOp);
}
mFrameTableWriter.EndArray();
JS::Rooted<JSScript*> script(mContext);
jsbytecode* pc;
mFrameTableWriter.StartObjectProperty("attempts");
{
{
JSONSchemaWriter schema(mFrameTableWriter);
schema.WriteField("strategy");
schema.WriteField("outcome");
}
mFrameTableWriter.StartArrayProperty("data");
{
StreamOptimizationAttemptsOp attemptOp(mFrameTableWriter, mUniqueStrings);
jitFrame.forEachOptimizationAttempt(attemptOp, script.address(), &pc);
}
mFrameTableWriter.EndArray();
}
mFrameTableWriter.EndObject();
if (JSAtom* name = js::GetPropertyNameFromPC(script, pc)) {
char buf[512];
JS_PutEscapedFlatString(buf, mozilla::ArrayLength(buf), js::AtomToFlatString(name), 0);
mUniqueStrings.WriteProperty(mFrameTableWriter, "propertyName", buf);
}
unsigned line, column;
line = JS_PCToLineNumber(script, pc, &column);
mFrameTableWriter.IntProperty("line", line);
mFrameTableWriter.IntProperty("column", column);
}
mFrameTableWriter.EndObject();
}
}
#endif
}
struct ProfileSample
{
uint32_t mStack;
Maybe<double> mTime;
Maybe<double> mResponsiveness;
Maybe<double> mRSS;
Maybe<double> mUSS;
Maybe<int> mFrameNumber;
Maybe<double> mPower;
};
static void WriteSample(SpliceableJSONWriter& aWriter, ProfileSample& aSample)
{
enum Schema : uint32_t {
STACK = 0,
TIME = 1,
RESPONSIVENESS = 2,
RSS = 3,
USS = 4,
FRAME_NUMBER = 5,
POWER = 6
};
AutoArraySchemaWriter writer(aWriter);
writer.IntElement(STACK, aSample.mStack);
if (aSample.mTime.isSome()) {
writer.DoubleElement(TIME, *aSample.mTime);
}
if (aSample.mResponsiveness.isSome()) {
writer.DoubleElement(RESPONSIVENESS, *aSample.mResponsiveness);
}
if (aSample.mRSS.isSome()) {
writer.DoubleElement(RSS, *aSample.mRSS);
}
if (aSample.mUSS.isSome()) {
writer.DoubleElement(USS, *aSample.mUSS);
}
if (aSample.mFrameNumber.isSome()) {
writer.IntElement(FRAME_NUMBER, *aSample.mFrameNumber);
}
if (aSample.mPower.isSome()) {
writer.DoubleElement(POWER, *aSample.mPower);
}
}
void ProfileBuffer::StreamSamplesToJSON(SpliceableJSONWriter& aWriter, int aThreadId,
double aSinceTime, JSContext* aContext,
UniqueStacks& aUniqueStacks)
{
Maybe<ProfileSample> sample;
int readPos = mReadPos;
int currentThreadID = -1;
Maybe<double> currentTime;
UniquePtr<char[]> tagBuff = MakeUnique<char[]>(DYNAMIC_MAX_STRING);
while (readPos != mWritePos) {
ProfileEntry entry = mEntries[readPos];
if (entry.mTagName == 'T') {
currentThreadID = entry.mTagInt;
currentTime.reset();
int readAheadPos = (readPos + 1) % mEntrySize;
if (readAheadPos != mWritePos) {
ProfileEntry readAheadEntry = mEntries[readAheadPos];
if (readAheadEntry.mTagName == 't') {
currentTime = Some(readAheadEntry.mTagDouble);
}
}
}
if (currentThreadID == aThreadId && (currentTime.isNothing() || *currentTime >= aSinceTime)) {
switch (entry.mTagName) {
case 'r':
if (sample.isSome()) {
sample->mResponsiveness = Some(entry.mTagDouble);
}
break;
case 'p':
if (sample.isSome()) {
sample->mPower = Some(entry.mTagDouble);
}
break;
case 'R':
if (sample.isSome()) {
sample->mRSS = Some(entry.mTagDouble);
}
break;
case 'U':
if (sample.isSome()) {
sample->mUSS = Some(entry.mTagDouble);
}
break;
case 'f':
if (sample.isSome()) {
sample->mFrameNumber = Some(entry.mTagInt);
}
break;
case 's':
{
// end the previous sample if there was one
if (sample.isSome()) {
WriteSample(aWriter, *sample);
sample.reset();
}
// begin the next sample
sample.emplace();
sample->mTime = currentTime;
// Seek forward through the entire sample, looking for frames
// this is an easier approach to reason about than adding more
// control variables and cases to the loop that goes through the buffer once
UniqueStacks::Stack stack =
aUniqueStacks.BeginStack(UniqueStacks::OnStackFrameKey("(root)"));
int framePos = (readPos + 1) % mEntrySize;
ProfileEntry frame = mEntries[framePos];
while (framePos != mWritePos && frame.mTagName != 's' && frame.mTagName != 'T') {
int incBy = 1;
frame = mEntries[framePos];
// Read ahead to the next tag, if it's a 'd' tag process it now
const char* tagStringData = frame.mTagData;
int readAheadPos = (framePos + 1) % mEntrySize;
// Make sure the string is always null terminated if it fills up
// DYNAMIC_MAX_STRING-2
tagBuff[DYNAMIC_MAX_STRING-1] = '\0';
if (readAheadPos != mWritePos && mEntries[readAheadPos].mTagName == 'd') {
tagStringData = processDynamicTag(framePos, &incBy, tagBuff.get());
}
// Write one frame. It can have either
// 1. only location - 'l' containing a memory address
// 2. location and line number - 'c' followed by 'd's,
// an optional 'n' and an optional 'y'
// 3. a JIT return address - 'j' containing native code address
if (frame.mTagName == 'l') {
// Bug 753041
// We need a double cast here to tell GCC that we don't want to sign
// extend 32-bit addresses starting with 0xFXXXXXX.
unsigned long long pc = (unsigned long long)(uintptr_t)frame.mTagPtr;
snprintf(tagBuff.get(), DYNAMIC_MAX_STRING, "%#llx", pc);
stack.AppendFrame(UniqueStacks::OnStackFrameKey(tagBuff.get()));
} else if (frame.mTagName == 'c') {
UniqueStacks::OnStackFrameKey frameKey(tagStringData);
readAheadPos = (framePos + incBy) % mEntrySize;
if (readAheadPos != mWritePos &&
mEntries[readAheadPos].mTagName == 'n') {
frameKey.mLine = Some((unsigned) mEntries[readAheadPos].mTagInt);
incBy++;
}
readAheadPos = (framePos + incBy) % mEntrySize;
if (readAheadPos != mWritePos &&
mEntries[readAheadPos].mTagName == 'y') {
frameKey.mCategory = Some((unsigned) mEntries[readAheadPos].mTagInt);
incBy++;
}
stack.AppendFrame(frameKey);
#ifndef SPS_STANDALONE
} else if (frame.mTagName == 'J') {
// A JIT frame may expand to multiple frames due to inlining.
void* pc = frame.mTagPtr;
unsigned depth = aUniqueStacks.LookupJITFrameDepth(pc);
if (depth == 0) {
StreamJSFramesOp framesOp(pc, stack);
JS::ForEachProfiledFrame(aContext, pc, framesOp);
aUniqueStacks.AddJITFrameDepth(pc, framesOp.depth());
} else {
for (unsigned i = 0; i < depth; i++) {
UniqueStacks::OnStackFrameKey inlineFrameKey(pc, i);
stack.AppendFrame(inlineFrameKey);
}
}
#endif
}
framePos = (framePos + incBy) % mEntrySize;
}
sample->mStack = stack.GetOrAddIndex();
break;
}
}
}
readPos = (readPos + 1) % mEntrySize;
}
if (sample.isSome()) {
WriteSample(aWriter, *sample);
}
}
void ProfileBuffer::StreamMarkersToJSON(SpliceableJSONWriter& aWriter, int aThreadId,
double aSinceTime, UniqueStacks& aUniqueStacks)
{
int readPos = mReadPos;
int currentThreadID = -1;
while (readPos != mWritePos) {
ProfileEntry entry = mEntries[readPos];
if (entry.mTagName == 'T') {
currentThreadID = entry.mTagInt;
} else if (currentThreadID == aThreadId && entry.mTagName == 'm') {
const ProfilerMarker* marker = entry.getMarker();
if (marker->GetTime() >= aSinceTime) {
entry.getMarker()->StreamJSON(aWriter, aUniqueStacks);
}
}
readPos = (readPos + 1) % mEntrySize;
}
}
int ProfileBuffer::FindLastSampleOfThread(int aThreadId)
{
// We search backwards from mWritePos-1 to mReadPos.
// Adding mEntrySize makes the result of the modulus positive.
for (int readPos = (mWritePos + mEntrySize - 1) % mEntrySize;
readPos != (mReadPos + mEntrySize - 1) % mEntrySize;
readPos = (readPos + mEntrySize - 1) % mEntrySize) {
ProfileEntry entry = mEntries[readPos];
if (entry.mTagName == 'T' && entry.mTagInt == aThreadId) {
return readPos;
}
}
return -1;
}
void ProfileBuffer::DuplicateLastSample(int aThreadId)
{
int lastSampleStartPos = FindLastSampleOfThread(aThreadId);
if (lastSampleStartPos == -1) {
return;
}
MOZ_ASSERT(mEntries[lastSampleStartPos].mTagName == 'T');
addTag(mEntries[lastSampleStartPos]);
// Go through the whole entry and duplicate it, until we find the next one.
for (int readPos = (lastSampleStartPos + 1) % mEntrySize;
readPos != mWritePos;
readPos = (readPos + 1) % mEntrySize) {
switch (mEntries[readPos].mTagName) {
case 'T':
// We're done.
return;
case 't':
// Copy with new time
addTag(ProfileEntry('t', (mozilla::TimeStamp::Now() - sStartTime).ToMilliseconds()));
break;
case 'm':
// Don't copy markers
break;
// Copy anything else we don't know about
// L, B, S, c, s, d, l, f, h, r, t, p
default:
addTag(mEntries[readPos]);
break;
}
}
}
// END ProfileBuffer
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
// BEGIN ThreadProfile
// END ThreadProfile
////////////////////////////////////////////////////////////////////////

View file

@ -1,407 +0,0 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef MOZ_PROFILE_ENTRY_H
#define MOZ_PROFILE_ENTRY_H
#include <ostream>
#include "GeckoProfiler.h"
#include "platform.h"
#include "ProfileJSONWriter.h"
#include "ProfilerBacktrace.h"
#include "mozilla/RefPtr.h"
#include <string>
#include <map>
#ifndef SPS_STANDALONE
#include "js/ProfilingFrameIterator.h"
#include "js/TrackedOptimizationInfo.h"
#include "nsHashKeys.h"
#include "nsDataHashtable.h"
#endif
#include "mozilla/Maybe.h"
#include "mozilla/Vector.h"
#ifndef SPS_STANDALONE
#include "gtest/MozGtestFriend.h"
#else
#define FRIEND_TEST(a, b) // TODO Support standalone gtest
#endif
#include "mozilla/HashFunctions.h"
#include "mozilla/UniquePtr.h"
class ThreadProfile;
// NB: Packing this structure has been shown to cause SIGBUS issues on ARM.
#ifndef __arm__
#pragma pack(push, 1)
#endif
class ProfileEntry
{
public:
ProfileEntry();
// aTagData must not need release (i.e. be a string from the text segment)
ProfileEntry(char aTagName, const char *aTagData);
ProfileEntry(char aTagName, void *aTagPtr);
ProfileEntry(char aTagName, ProfilerMarker *aTagMarker);
ProfileEntry(char aTagName, double aTagDouble);
ProfileEntry(char aTagName, uintptr_t aTagOffset);
ProfileEntry(char aTagName, Address aTagAddress);
ProfileEntry(char aTagName, int aTagLine);
ProfileEntry(char aTagName, char aTagChar);
bool is_ent_hint(char hintChar);
bool is_ent_hint();
bool is_ent(char tagName);
void* get_tagPtr();
const ProfilerMarker* getMarker() {
MOZ_ASSERT(mTagName == 'm');
return mTagMarker;
}
char getTagName() const { return mTagName; }
private:
FRIEND_TEST(ThreadProfile, InsertOneTag);
FRIEND_TEST(ThreadProfile, InsertOneTagWithTinyBuffer);
FRIEND_TEST(ThreadProfile, InsertTagsNoWrap);
FRIEND_TEST(ThreadProfile, InsertTagsWrap);
FRIEND_TEST(ThreadProfile, MemoryMeasure);
friend class ProfileBuffer;
union {
const char* mTagData;
char mTagChars[sizeof(void*)];
void* mTagPtr;
ProfilerMarker* mTagMarker;
double mTagDouble;
Address mTagAddress;
uintptr_t mTagOffset;
int mTagInt;
char mTagChar;
};
char mTagName;
};
#ifndef __arm__
#pragma pack(pop)
#endif
class UniqueJSONStrings
{
public:
UniqueJSONStrings() {
mStringTableWriter.StartBareList();
}
void SpliceStringTableElements(SpliceableJSONWriter& aWriter) {
aWriter.TakeAndSplice(mStringTableWriter.WriteFunc());
}
void WriteProperty(mozilla::JSONWriter& aWriter, const char* aName, const char* aStr) {
aWriter.IntProperty(aName, GetOrAddIndex(aStr));
}
void WriteElement(mozilla::JSONWriter& aWriter, const char* aStr) {
aWriter.IntElement(GetOrAddIndex(aStr));
}
uint32_t GetOrAddIndex(const char* aStr);
struct StringKey {
explicit StringKey(const char* aStr)
: mStr(strdup(aStr))
{
mHash = mozilla::HashString(mStr);
}
StringKey(const StringKey& aOther)
: mStr(strdup(aOther.mStr))
{
mHash = aOther.mHash;
}
~StringKey() {
free(mStr);
}
uint32_t Hash() const;
bool operator==(const StringKey& aOther) const {
return strcmp(mStr, aOther.mStr) == 0;
}
bool operator<(const StringKey& aOther) const {
return mHash < aOther.mHash;
}
private:
uint32_t mHash;
char* mStr;
};
private:
SpliceableChunkedJSONWriter mStringTableWriter;
std::map<StringKey, uint32_t> mStringToIndexMap;
};
class UniqueStacks
{
public:
struct FrameKey {
#ifdef SPS_STANDALONE
std::string mLocation;
#else
// This cannot be a std::string, as it is not memmove compatible, which
// is used by nsHashTable
nsCString mLocation;
#endif
mozilla::Maybe<unsigned> mLine;
mozilla::Maybe<unsigned> mCategory;
mozilla::Maybe<void*> mJITAddress;
mozilla::Maybe<uint32_t> mJITDepth;
explicit FrameKey(const char* aLocation)
: mLocation(aLocation)
{
mHash = Hash();
}
FrameKey(const FrameKey& aToCopy)
: mLocation(aToCopy.mLocation)
, mLine(aToCopy.mLine)
, mCategory(aToCopy.mCategory)
, mJITAddress(aToCopy.mJITAddress)
, mJITDepth(aToCopy.mJITDepth)
{
mHash = Hash();
}
FrameKey(void* aJITAddress, uint32_t aJITDepth)
: mJITAddress(mozilla::Some(aJITAddress))
, mJITDepth(mozilla::Some(aJITDepth))
{
mHash = Hash();
}
uint32_t Hash() const;
bool operator==(const FrameKey& aOther) const;
bool operator<(const FrameKey& aOther) const {
return mHash < aOther.mHash;
}
private:
uint32_t mHash;
};
// A FrameKey that holds a scoped reference to a JIT FrameHandle.
struct MOZ_STACK_CLASS OnStackFrameKey : public FrameKey {
explicit OnStackFrameKey(const char* aLocation)
: FrameKey(aLocation)
#ifndef SPS_STANDALONE
, mJITFrameHandle(nullptr)
#endif
{ }
OnStackFrameKey(const OnStackFrameKey& aToCopy)
: FrameKey(aToCopy)
#ifndef SPS_STANDALONE
, mJITFrameHandle(aToCopy.mJITFrameHandle)
#endif
{ }
#ifndef SPS_STANDALONE
const JS::ForEachProfiledFrameOp::FrameHandle* mJITFrameHandle;
OnStackFrameKey(void* aJITAddress, unsigned aJITDepth)
: FrameKey(aJITAddress, aJITDepth)
, mJITFrameHandle(nullptr)
{ }
OnStackFrameKey(void* aJITAddress, unsigned aJITDepth,
const JS::ForEachProfiledFrameOp::FrameHandle& aJITFrameHandle)
: FrameKey(aJITAddress, aJITDepth)
, mJITFrameHandle(&aJITFrameHandle)
{ }
#endif
};
struct StackKey {
mozilla::Maybe<uint32_t> mPrefixHash;
mozilla::Maybe<uint32_t> mPrefix;
uint32_t mFrame;
explicit StackKey(uint32_t aFrame)
: mFrame(aFrame)
{
mHash = Hash();
}
uint32_t Hash() const;
bool operator==(const StackKey& aOther) const;
bool operator<(const StackKey& aOther) const {
return mHash < aOther.mHash;
}
void UpdateHash(uint32_t aPrefixHash, uint32_t aPrefix, uint32_t aFrame) {
mPrefixHash = mozilla::Some(aPrefixHash);
mPrefix = mozilla::Some(aPrefix);
mFrame = aFrame;
mHash = Hash();
}
private:
uint32_t mHash;
};
class Stack {
public:
Stack(UniqueStacks& aUniqueStacks, const OnStackFrameKey& aRoot);
void AppendFrame(const OnStackFrameKey& aFrame);
uint32_t GetOrAddIndex() const;
private:
UniqueStacks& mUniqueStacks;
StackKey mStack;
};
explicit UniqueStacks(JSContext* aContext);
Stack BeginStack(const OnStackFrameKey& aRoot);
uint32_t LookupJITFrameDepth(void* aAddr);
void AddJITFrameDepth(void* aAddr, unsigned depth);
void SpliceFrameTableElements(SpliceableJSONWriter& aWriter);
void SpliceStackTableElements(SpliceableJSONWriter& aWriter);
private:
uint32_t GetOrAddFrameIndex(const OnStackFrameKey& aFrame);
uint32_t GetOrAddStackIndex(const StackKey& aStack);
void StreamFrame(const OnStackFrameKey& aFrame);
void StreamStack(const StackKey& aStack);
public:
UniqueJSONStrings mUniqueStrings;
private:
JSContext* mContext;
// To avoid incurring JitcodeGlobalTable lookup costs for every JIT frame,
// we cache the depth of frames keyed by JIT code address. If an address a
// maps to a depth d, then frames keyed by a for depths 0 to d are
// guaranteed to be in mFrameToIndexMap.
std::map<void*, uint32_t> mJITFrameDepthMap;
uint32_t mFrameCount;
SpliceableChunkedJSONWriter mFrameTableWriter;
#ifdef SPS_STANDALNOE
std::map<FrameKey, uint32_t> mFrameToIndexMap;
#else
nsDataHashtable<nsGenericHashKey<FrameKey>, uint32_t> mFrameToIndexMap;
#endif
SpliceableChunkedJSONWriter mStackTableWriter;
// This sucks but this is really performance critical, nsDataHashtable is way faster
// than map/unordered_map but nsDataHashtable is tied to xpcom so we ifdef
// until we can find a better solution.
#ifdef SPS_STANDALONE
std::map<StackKey, uint32_t> mStackToIndexMap;
#else
nsDataHashtable<nsGenericHashKey<StackKey>, uint32_t> mStackToIndexMap;
#endif
};
//
// ThreadProfile JSON Format
// -------------------------
//
// The profile contains much duplicate information. The output JSON of the
// profile attempts to deduplicate strings, frames, and stack prefixes, to cut
// down on size and to increase JSON streaming speed. Deduplicated values are
// streamed as indices into their respective tables.
//
// Further, arrays of objects with the same set of properties (e.g., samples,
// frames) are output as arrays according to a schema instead of an object
// with property names. A property that is not present is represented in the
// array as null or undefined.
//
// The format of the thread profile JSON is shown by the following example
// with 1 sample and 1 marker:
//
// {
// "name": "Foo",
// "tid": 42,
// "samples":
// {
// "schema":
// {
// "stack": 0, /* index into stackTable */
// "time": 1, /* number */
// "responsiveness": 2, /* number */
// "rss": 3, /* number */
// "uss": 4, /* number */
// "frameNumber": 5, /* number */
// "power": 6 /* number */
// },
// "data":
// [
// [ 1, 0.0, 0.0 ] /* { stack: 1, time: 0.0, responsiveness: 0.0 } */
// ]
// },
//
// "markers":
// {
// "schema":
// {
// "name": 0, /* index into stringTable */
// "time": 1, /* number */
// "data": 2 /* arbitrary JSON */
// },
// "data":
// [
// [ 3, 0.1 ] /* { name: 'example marker', time: 0.1 } */
// ]
// },
//
// "stackTable":
// {
// "schema":
// {
// "prefix": 0, /* index into stackTable */
// "frame": 1 /* index into frameTable */
// },
// "data":
// [
// [ null, 0 ], /* (root) */
// [ 0, 1 ] /* (root) > foo.js */
// ]
// },
//
// "frameTable":
// {
// "schema":
// {
// "location": 0, /* index into stringTable */
// "implementation": 1, /* index into stringTable */
// "optimizations": 2, /* arbitrary JSON */
// "line": 3, /* number */
// "category": 4 /* number */
// },
// "data":
// [
// [ 0 ], /* { location: '(root)' } */
// [ 1, 2 ] /* { location: 'foo.js', implementation: 'baseline' } */
// ]
// },
//
// "stringTable":
// [
// "(root)",
// "foo.js",
// "baseline",
// "example marker"
// ]
// }
//
#endif /* ndef MOZ_PROFILE_ENTRY_H */

View file

@ -1,115 +0,0 @@
/* -*- 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 "mozilla/HashFunctions.h"
#include "ProfileJSONWriter.h"
void
ChunkedJSONWriteFunc::Write(const char* aStr)
{
MOZ_ASSERT(mChunkPtr >= mChunkList.back().get() && mChunkPtr <= mChunkEnd);
MOZ_ASSERT(mChunkEnd >= mChunkList.back().get() + mChunkLengths.back());
MOZ_ASSERT(*mChunkPtr == '\0');
size_t len = strlen(aStr);
// Most strings to be written are small, but subprocess profiles (e.g.,
// from the content process in e10s) may be huge. If the string is larger
// than a chunk, allocate its own chunk.
char* newPtr;
if (len >= kChunkSize) {
AllocChunk(len + 1);
newPtr = mChunkPtr + len;
} else {
newPtr = mChunkPtr + len;
if (newPtr >= mChunkEnd) {
AllocChunk(kChunkSize);
newPtr = mChunkPtr + len;
}
}
memcpy(mChunkPtr, aStr, len);
*newPtr = '\0';
mChunkPtr = newPtr;
mChunkLengths.back() += len;
}
mozilla::UniquePtr<char[]>
ChunkedJSONWriteFunc::CopyData() const
{
MOZ_ASSERT(mChunkLengths.length() == mChunkList.length());
size_t totalLen = 1;
for (size_t i = 0; i < mChunkLengths.length(); i++) {
MOZ_ASSERT(strlen(mChunkList[i].get()) == mChunkLengths[i]);
totalLen += mChunkLengths[i];
}
mozilla::UniquePtr<char[]> c = mozilla::MakeUnique<char[]>(totalLen);
char* ptr = c.get();
for (size_t i = 0; i < mChunkList.length(); i++) {
size_t len = mChunkLengths[i];
memcpy(ptr, mChunkList[i].get(), len);
ptr += len;
}
*ptr = '\0';
return c;
}
void
ChunkedJSONWriteFunc::Take(ChunkedJSONWriteFunc&& aOther)
{
for (size_t i = 0; i < aOther.mChunkList.length(); i++) {
MOZ_ALWAYS_TRUE(mChunkLengths.append(aOther.mChunkLengths[i]));
MOZ_ALWAYS_TRUE(mChunkList.append(mozilla::Move(aOther.mChunkList[i])));
}
mChunkPtr = mChunkList.back().get() + mChunkLengths.back();
mChunkEnd = mChunkPtr;
aOther.mChunkPtr = nullptr;
aOther.mChunkEnd = nullptr;
aOther.mChunkList.clear();
aOther.mChunkLengths.clear();
}
void
ChunkedJSONWriteFunc::AllocChunk(size_t aChunkSize)
{
MOZ_ASSERT(mChunkLengths.length() == mChunkList.length());
mozilla::UniquePtr<char[]> newChunk = mozilla::MakeUnique<char[]>(aChunkSize);
mChunkPtr = newChunk.get();
mChunkEnd = mChunkPtr + aChunkSize;
*mChunkPtr = '\0';
MOZ_ALWAYS_TRUE(mChunkLengths.append(0));
MOZ_ALWAYS_TRUE(mChunkList.append(mozilla::Move(newChunk)));
}
void
SpliceableJSONWriter::TakeAndSplice(ChunkedJSONWriteFunc* aFunc)
{
Separator();
for (size_t i = 0; i < aFunc->mChunkList.length(); i++) {
WriteFunc()->Write(aFunc->mChunkList[i].get());
}
aFunc->mChunkPtr = nullptr;
aFunc->mChunkEnd = nullptr;
aFunc->mChunkList.clear();
aFunc->mChunkLengths.clear();
mNeedComma[mDepth] = true;
}
void
SpliceableJSONWriter::Splice(const char* aStr)
{
Separator();
WriteFunc()->Write(aStr);
mNeedComma[mDepth] = true;
}
void
SpliceableChunkedJSONWriter::TakeAndSplice(ChunkedJSONWriteFunc* aFunc)
{
Separator();
WriteFunc()->Take(mozilla::Move(*aFunc));
mNeedComma[mDepth] = true;
}

View file

@ -1,126 +0,0 @@
/* -*- 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/. */
#ifndef PROFILEJSONWRITER_H
#define PROFILEJSONWRITER_H
#include <ostream>
#include <string>
#include <string.h>
#include "mozilla/JSONWriter.h"
#include "mozilla/UniquePtr.h"
class SpliceableChunkedJSONWriter;
// On average, profile JSONs are large enough such that we want to avoid
// reallocating its buffer when expanding. Additionally, the contents of the
// profile are not accessed until the profile is entirely written. For these
// reasons we use a chunked writer that keeps an array of chunks, which is
// concatenated together after writing is finished.
class ChunkedJSONWriteFunc : public mozilla::JSONWriteFunc
{
public:
friend class SpliceableJSONWriter;
ChunkedJSONWriteFunc() {
AllocChunk(kChunkSize);
}
bool IsEmpty() const {
MOZ_ASSERT_IF(!mChunkPtr, !mChunkEnd &&
mChunkList.length() == 0 &&
mChunkLengths.length() == 0);
return !mChunkPtr;
}
void Write(const char* aStr) override;
mozilla::UniquePtr<char[]> CopyData() const;
void Take(ChunkedJSONWriteFunc&& aOther);
private:
void AllocChunk(size_t aChunkSize);
static const size_t kChunkSize = 4096 * 512;
// Pointer for writing inside the current chunk.
//
// The current chunk is always at the back of mChunkList, i.e.,
// mChunkList.back() <= mChunkPtr <= mChunkEnd.
char* mChunkPtr;
// Pointer to the end of the current chunk.
//
// The current chunk is always at the back of mChunkList, i.e.,
// mChunkEnd >= mChunkList.back() + mChunkLengths.back().
char* mChunkEnd;
// List of chunks and their lengths.
//
// For all i, the length of the string in mChunkList[i] is
// mChunkLengths[i].
mozilla::Vector<mozilla::UniquePtr<char[]>> mChunkList;
mozilla::Vector<size_t> mChunkLengths;
};
struct OStreamJSONWriteFunc : public mozilla::JSONWriteFunc
{
explicit OStreamJSONWriteFunc(std::ostream& aStream)
: mStream(aStream)
{ }
void Write(const char* aStr) override {
mStream << aStr;
}
std::ostream& mStream;
};
class SpliceableJSONWriter : public mozilla::JSONWriter
{
public:
explicit SpliceableJSONWriter(mozilla::UniquePtr<mozilla::JSONWriteFunc> aWriter)
: JSONWriter(mozilla::Move(aWriter))
{ }
void StartBareList(CollectionStyle aStyle = SingleLineStyle) {
StartCollection(nullptr, "", aStyle);
}
void EndBareList() {
EndCollection("");
}
void NullElements(uint32_t aCount) {
for (uint32_t i = 0; i < aCount; i++) {
NullElement();
}
}
void Splice(const ChunkedJSONWriteFunc* aFunc);
void Splice(const char* aStr);
// Takes the chunks from aFunc and write them. If move is not possible
// (e.g., using OStreamJSONWriteFunc), aFunc's chunks are copied and its
// storage cleared.
virtual void TakeAndSplice(ChunkedJSONWriteFunc* aFunc);
};
class SpliceableChunkedJSONWriter : public SpliceableJSONWriter
{
public:
explicit SpliceableChunkedJSONWriter()
: SpliceableJSONWriter(mozilla::MakeUnique<ChunkedJSONWriteFunc>())
{ }
ChunkedJSONWriteFunc* WriteFunc() const {
return static_cast<ChunkedJSONWriteFunc*>(JSONWriter::WriteFunc());
}
// Adopts the chunks from aFunc without copying.
virtual void TakeAndSplice(ChunkedJSONWriteFunc* aFunc) override;
};
#endif // PROFILEJSONWRITER_H

View file

@ -1,33 +0,0 @@
/* -*- 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 "ProfilerBacktrace.h"
#include "ProfileJSONWriter.h"
#include "SyncProfile.h"
ProfilerBacktrace::ProfilerBacktrace(SyncProfile* aProfile)
: mProfile(aProfile)
{
MOZ_COUNT_CTOR(ProfilerBacktrace);
MOZ_ASSERT(aProfile);
}
ProfilerBacktrace::~ProfilerBacktrace()
{
MOZ_COUNT_DTOR(ProfilerBacktrace);
if (mProfile->ShouldDestroy()) {
delete mProfile;
}
}
void
ProfilerBacktrace::StreamJSON(SpliceableJSONWriter& aWriter,
UniqueStacks& aUniqueStacks)
{
::MutexAutoLock lock(mProfile->GetMutex());
mProfile->StreamJSON(aWriter, aUniqueStacks);
}

View file

@ -1,210 +0,0 @@
/* -*- 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 "GeckoProfiler.h"
#include "ProfilerBacktrace.h"
#include "ProfilerMarkers.h"
#include "SyncProfile.h"
#ifndef SPS_STANDALONE
#include "gfxASurface.h"
#include "Layers.h"
#include "mozilla/Sprintf.h"
#endif
ProfilerMarkerPayload::ProfilerMarkerPayload(ProfilerBacktrace* aStack)
: mStack(aStack)
{}
ProfilerMarkerPayload::ProfilerMarkerPayload(const mozilla::TimeStamp& aStartTime,
const mozilla::TimeStamp& aEndTime,
ProfilerBacktrace* aStack)
: mStartTime(aStartTime)
, mEndTime(aEndTime)
, mStack(aStack)
{}
ProfilerMarkerPayload::~ProfilerMarkerPayload()
{
profiler_free_backtrace(mStack);
}
void
ProfilerMarkerPayload::streamCommonProps(const char* aMarkerType,
SpliceableJSONWriter& aWriter,
UniqueStacks& aUniqueStacks)
{
MOZ_ASSERT(aMarkerType);
aWriter.StringProperty("type", aMarkerType);
if (!mStartTime.IsNull()) {
aWriter.DoubleProperty("startTime", profiler_time(mStartTime));
}
if (!mEndTime.IsNull()) {
aWriter.DoubleProperty("endTime", profiler_time(mEndTime));
}
if (mStack) {
aWriter.StartObjectProperty("stack");
{
mStack->StreamJSON(aWriter, aUniqueStacks);
}
aWriter.EndObject();
}
}
ProfilerMarkerTracing::ProfilerMarkerTracing(const char* aCategory, TracingMetadata aMetaData)
: mCategory(aCategory)
, mMetaData(aMetaData)
{
if (aMetaData == TRACING_EVENT_BACKTRACE) {
SetStack(profiler_get_backtrace());
}
}
ProfilerMarkerTracing::ProfilerMarkerTracing(const char* aCategory, TracingMetadata aMetaData,
ProfilerBacktrace* aCause)
: mCategory(aCategory)
, mMetaData(aMetaData)
{
if (aCause) {
SetStack(aCause);
}
}
void
ProfilerMarkerTracing::StreamPayload(SpliceableJSONWriter& aWriter,
UniqueStacks& aUniqueStacks)
{
streamCommonProps("tracing", aWriter, aUniqueStacks);
if (GetCategory()) {
aWriter.StringProperty("category", GetCategory());
}
if (GetMetaData() != TRACING_DEFAULT) {
if (GetMetaData() == TRACING_INTERVAL_START) {
aWriter.StringProperty("interval", "start");
} else if (GetMetaData() == TRACING_INTERVAL_END) {
aWriter.StringProperty("interval", "end");
}
}
}
#ifndef SPS_STANDALONE
GPUMarkerPayload::GPUMarkerPayload(
const mozilla::TimeStamp& aCpuTimeStart,
const mozilla::TimeStamp& aCpuTimeEnd,
uint64_t aGpuTimeStart,
uint64_t aGpuTimeEnd)
: ProfilerMarkerPayload(aCpuTimeStart, aCpuTimeEnd)
, mCpuTimeStart(aCpuTimeStart)
, mCpuTimeEnd(aCpuTimeEnd)
, mGpuTimeStart(aGpuTimeStart)
, mGpuTimeEnd(aGpuTimeEnd)
{ }
void
GPUMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter,
UniqueStacks& aUniqueStacks)
{
streamCommonProps("gpu_timer_query", aWriter, aUniqueStacks);
aWriter.DoubleProperty("cpustart", profiler_time(mCpuTimeStart));
aWriter.DoubleProperty("cpuend", profiler_time(mCpuTimeEnd));
aWriter.IntProperty("gpustart", (int)mGpuTimeStart);
aWriter.IntProperty("gpuend", (int)mGpuTimeEnd);
}
ProfilerMarkerImagePayload::ProfilerMarkerImagePayload(gfxASurface *aImg)
: mImg(aImg)
{ }
void
ProfilerMarkerImagePayload::StreamPayload(SpliceableJSONWriter& aWriter,
UniqueStacks& aUniqueStacks)
{
streamCommonProps("innerHTML", aWriter, aUniqueStacks);
// TODO: Finish me
//aWriter.NameValue("innerHTML", "<img src=''/>");
}
IOMarkerPayload::IOMarkerPayload(const char* aSource,
const char* aFilename,
const mozilla::TimeStamp& aStartTime,
const mozilla::TimeStamp& aEndTime,
ProfilerBacktrace* aStack)
: ProfilerMarkerPayload(aStartTime, aEndTime, aStack),
mSource(aSource)
{
mFilename = aFilename ? strdup(aFilename) : nullptr;
MOZ_ASSERT(aSource);
}
IOMarkerPayload::~IOMarkerPayload(){
free(mFilename);
}
void
IOMarkerPayload::StreamPayload(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks)
{
streamCommonProps("io", aWriter, aUniqueStacks);
aWriter.StringProperty("source", mSource);
if (mFilename != nullptr) {
aWriter.StringProperty("filename", mFilename);
}
}
void
ProfilerJSEventMarker(const char *event)
{
PROFILER_MARKER(event);
}
LayerTranslationPayload::LayerTranslationPayload(mozilla::layers::Layer* aLayer,
mozilla::gfx::Point aPoint)
: ProfilerMarkerPayload(mozilla::TimeStamp::Now(), mozilla::TimeStamp::Now(), nullptr)
, mLayer(aLayer)
, mPoint(aPoint)
{
}
void
LayerTranslationPayload::StreamPayload(SpliceableJSONWriter& aWriter,
UniqueStacks& aUniqueStacks)
{
const size_t bufferSize = 32;
char buffer[bufferSize];
SprintfLiteral(buffer, "%p", mLayer);
aWriter.StringProperty("layer", buffer);
aWriter.IntProperty("x", mPoint.x);
aWriter.IntProperty("y", mPoint.y);
aWriter.StringProperty("category", "LayerTranslation");
}
TouchDataPayload::TouchDataPayload(const mozilla::ScreenIntPoint& aPoint)
: ProfilerMarkerPayload(mozilla::TimeStamp::Now(), mozilla::TimeStamp::Now(), nullptr)
{
mPoint = aPoint;
}
void
TouchDataPayload::StreamPayload(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks)
{
aWriter.IntProperty("x", mPoint.x);
aWriter.IntProperty("y", mPoint.y);
}
VsyncPayload::VsyncPayload(mozilla::TimeStamp aVsyncTimestamp)
: ProfilerMarkerPayload(aVsyncTimestamp, aVsyncTimestamp, nullptr)
, mVsyncTimestamp(aVsyncTimestamp)
{
}
void
VsyncPayload::StreamPayload(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks)
{
aWriter.DoubleProperty("vsync", profiler_time(mVsyncTimestamp));
aWriter.StringProperty("category", "VsyncTimestamp");
}
#endif

View file

@ -1,48 +0,0 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifdef XP_MACOSX
#include <mach/task.h>
#include <mach/thread_act.h>
#include <pthread.h>
#elif XP_WIN
#include <windows.h>
#endif
#include "StackTop.h"
void *GetStackTop(void *guess) {
#if defined(XP_MACOSX)
pthread_t thread = pthread_self();
return pthread_get_stackaddr_np(thread);
#elif defined(XP_WIN)
#if defined(_MSC_VER) && defined(_M_IX86)
// offset 0x18 from the FS segment register gives a pointer to
// the thread information block for the current thread
NT_TIB* pTib;
__asm {
MOV EAX, FS:[18h]
MOV pTib, EAX
}
return static_cast<void*>(pTib->StackBase);
#elif defined(__GNUC__) && defined(i386)
// offset 0x18 from the FS segment register gives a pointer to
// the thread information block for the current thread
NT_TIB* pTib;
asm ( "movl %%fs:0x18, %0\n"
: "=r" (pTib)
);
return static_cast<void*>(pTib->StackBase);
#elif defined(_M_X64) || defined(__x86_64)
PNT_TIB64 pTib = reinterpret_cast<PNT_TIB64>(NtCurrentTeb());
return reinterpret_cast<void*>(pTib->StackBase);
#else
#error Need a way to get the stack bounds on this platform (Windows)
#endif
#else
return guess;
#endif
}

View file

@ -1,10 +0,0 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef MOZ_STACK_TOP_H
#define MOZ_STACK_TOP_H
void *GetStackTop(void *guess);
#endif

View file

@ -1,57 +0,0 @@
/* -*- Mode: C++; tab-width: 2; 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 "SyncProfile.h"
SyncProfile::SyncProfile(ThreadInfo* aInfo, int aEntrySize)
: ThreadProfile(aInfo, new ProfileBuffer(aEntrySize))
, mOwnerState(REFERENCED)
{
MOZ_COUNT_CTOR(SyncProfile);
}
SyncProfile::~SyncProfile()
{
MOZ_COUNT_DTOR(SyncProfile);
// SyncProfile owns the ThreadInfo; see NewSyncProfile.
ThreadInfo* info = GetThreadInfo();
delete info;
}
bool
SyncProfile::ShouldDestroy()
{
::MutexAutoLock lock(GetMutex());
if (mOwnerState == OWNED) {
mOwnerState = OWNER_DESTROYING;
return true;
}
mOwnerState = ORPHANED;
return false;
}
void
SyncProfile::EndUnwind()
{
if (mOwnerState != ORPHANED) {
mOwnerState = OWNED;
}
// Save mOwnerState before we release the mutex
OwnerState ownerState = mOwnerState;
ThreadProfile::EndUnwind();
if (ownerState == ORPHANED) {
delete this;
}
}
// SyncProfiles' stacks are deduplicated in the context of the containing
// profile in which the backtrace is as a marker payload.
void
SyncProfile::StreamJSON(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks)
{
ThreadProfile::StreamSamplesAndMarkers(aWriter, /* aSinceTime = */ 0, aUniqueStacks);
}

View file

@ -1,43 +0,0 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef __SYNCPROFILE_H
#define __SYNCPROFILE_H
#include "ProfileEntry.h"
#include "ThreadProfile.h"
class SyncProfile : public ThreadProfile
{
public:
SyncProfile(ThreadInfo* aInfo, int aEntrySize);
~SyncProfile();
// SyncProfiles' stacks are deduplicated in the context of the containing
// profile in which the backtrace is as a marker payload.
void StreamJSON(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks);
virtual void EndUnwind();
virtual SyncProfile* AsSyncProfile() { return this; }
private:
friend class ProfilerBacktrace;
enum OwnerState
{
REFERENCED, // ProfilerBacktrace has a pointer to this but doesn't own
OWNED, // ProfilerBacktrace is responsible for destroying this
OWNER_DESTROYING, // ProfilerBacktrace owns this and is destroying
ORPHANED // No owner, we must destroy ourselves
};
bool ShouldDestroy();
OwnerState mOwnerState;
};
#endif // __SYNCPROFILE_H

View file

@ -1,73 +0,0 @@
/* -*- Mode: C++; tab-width: 2; 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 "ThreadInfo.h"
#include "ThreadProfile.h"
#include "mozilla/DebugOnly.h"
ThreadInfo::ThreadInfo(const char* aName, int aThreadId,
bool aIsMainThread, PseudoStack* aPseudoStack,
void* aStackTop)
: mName(strdup(aName))
, mThreadId(aThreadId)
, mIsMainThread(aIsMainThread)
, mPseudoStack(aPseudoStack)
, mPlatformData(Sampler::AllocPlatformData(aThreadId))
, mProfile(nullptr)
, mStackTop(aStackTop)
, mPendingDelete(false)
{
MOZ_COUNT_CTOR(ThreadInfo);
#ifndef SPS_STANDALONE
mThread = NS_GetCurrentThread();
#endif
// We don't have to guess on mac
#ifdef XP_MACOSX
pthread_t self = pthread_self();
mStackTop = pthread_get_stackaddr_np(self);
#endif
}
ThreadInfo::~ThreadInfo() {
MOZ_COUNT_DTOR(ThreadInfo);
free(mName);
if (mProfile)
delete mProfile;
Sampler::FreePlatformData(mPlatformData);
}
void
ThreadInfo::SetPendingDelete()
{
mPendingDelete = true;
// We don't own the pseudostack so disconnect it.
mPseudoStack = nullptr;
if (mProfile) {
mProfile->SetPendingDelete();
}
}
bool
ThreadInfo::CanInvokeJS() const
{
#ifdef SPS_STANDALONE
return false;
#else
nsIThread* thread = GetThread();
if (!thread) {
MOZ_ASSERT(IsMainThread());
return true;
}
bool result;
mozilla::DebugOnly<nsresult> rv = thread->GetCanInvokeJS(&result);
MOZ_ASSERT(NS_SUCCEEDED(rv));
return result;
#endif
}

View file

@ -1,66 +0,0 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef MOZ_THREAD_INFO_H
#define MOZ_THREAD_INFO_H
#include "platform.h"
class ThreadInfo {
public:
ThreadInfo(const char* aName, int aThreadId, bool aIsMainThread, PseudoStack* aPseudoStack, void* aStackTop);
virtual ~ThreadInfo();
const char* Name() const { return mName; }
int ThreadId() const { return mThreadId; }
bool IsMainThread() const { return mIsMainThread; }
PseudoStack* Stack() const { return mPseudoStack; }
void SetProfile(ThreadProfile* aProfile) { mProfile = aProfile; }
ThreadProfile* Profile() const { return mProfile; }
PlatformData* GetPlatformData() const { return mPlatformData; }
void* StackTop() const { return mStackTop; }
virtual void SetPendingDelete();
bool IsPendingDelete() const { return mPendingDelete; }
#ifndef SPS_STANDALONE
/**
* May be null for the main thread if the profiler was started during startup
*/
nsIThread* GetThread() const { return mThread.get(); }
#endif
bool CanInvokeJS() const;
private:
char* mName;
int mThreadId;
const bool mIsMainThread;
PseudoStack* mPseudoStack;
PlatformData* mPlatformData;
ThreadProfile* mProfile;
void* mStackTop;
#ifndef SPS_STANDALONE
nsCOMPtr<nsIThread> mThread;
#endif
bool mPendingDelete;
};
// Just like ThreadInfo, but owns a reference to the PseudoStack.
class StackOwningThreadInfo : public ThreadInfo {
public:
StackOwningThreadInfo(const char* aName, int aThreadId, bool aIsMainThread, PseudoStack* aPseudoStack, void* aStackTop);
virtual ~StackOwningThreadInfo();
virtual void SetPendingDelete();
};
#endif

View file

@ -1,260 +0,0 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
ThreadProfile::ThreadProfile(ThreadInfo* aInfo, ProfileBuffer* aBuffer)
: mThreadInfo(aInfo)
, mBuffer(aBuffer)
, mPseudoStack(aInfo->Stack())
, mMutex(OS::CreateMutex("ThreadProfile::mMutex"))
, mThreadId(int(aInfo->ThreadId()))
, mIsMainThread(aInfo->IsMainThread())
, mPlatformData(aInfo->GetPlatformData())
, mStackTop(aInfo->StackTop())
#ifndef SPS_STANDALONE
, mRespInfo(this)
#endif
#ifdef XP_LINUX
, mRssMemory(0)
, mUssMemory(0)
#endif
{
MOZ_COUNT_CTOR(ThreadProfile);
MOZ_ASSERT(aBuffer);
// I don't know if we can assert this. But we should warn.
MOZ_ASSERT(aInfo->ThreadId() >= 0, "native thread ID is < 0");
MOZ_ASSERT(aInfo->ThreadId() <= INT32_MAX, "native thread ID is > INT32_MAX");
}
ThreadProfile::~ThreadProfile()
{
MOZ_COUNT_DTOR(ThreadProfile);
}
void ThreadProfile::addTag(const ProfileEntry& aTag)
{
mBuffer->addTag(aTag);
}
void ThreadProfile::addStoredMarker(ProfilerMarker *aStoredMarker) {
mBuffer->addStoredMarker(aStoredMarker);
}
void ThreadProfile::StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime)
{
// mUniqueStacks may already be emplaced from FlushSamplesAndMarkers.
if (!mUniqueStacks.isSome()) {
#ifndef SPS_STANDALONE
mUniqueStacks.emplace(mPseudoStack->mContext);
#else
mUniqueStacks.emplace(nullptr);
#endif
}
aWriter.Start(SpliceableJSONWriter::SingleLineStyle);
{
StreamSamplesAndMarkers(aWriter, aSinceTime, *mUniqueStacks);
aWriter.StartObjectProperty("stackTable");
{
{
JSONSchemaWriter schema(aWriter);
schema.WriteField("prefix");
schema.WriteField("frame");
}
aWriter.StartArrayProperty("data");
{
mUniqueStacks->SpliceStackTableElements(aWriter);
}
aWriter.EndArray();
}
aWriter.EndObject();
aWriter.StartObjectProperty("frameTable");
{
{
JSONSchemaWriter schema(aWriter);
schema.WriteField("location");
schema.WriteField("implementation");
schema.WriteField("optimizations");
schema.WriteField("line");
schema.WriteField("category");
}
aWriter.StartArrayProperty("data");
{
mUniqueStacks->SpliceFrameTableElements(aWriter);
}
aWriter.EndArray();
}
aWriter.EndObject();
aWriter.StartArrayProperty("stringTable");
{
mUniqueStacks->mUniqueStrings.SpliceStringTableElements(aWriter);
}
aWriter.EndArray();
}
aWriter.End();
mUniqueStacks.reset();
}
void ThreadProfile::StreamSamplesAndMarkers(SpliceableJSONWriter& aWriter, double aSinceTime,
UniqueStacks& aUniqueStacks)
{
#ifndef SPS_STANDALONE
// Thread meta data
if (XRE_GetProcessType() == GeckoProcessType_Plugin) {
// TODO Add the proper plugin name
aWriter.StringProperty("name", "Plugin");
} else if (XRE_GetProcessType() == GeckoProcessType_Content) {
// This isn't going to really help once we have multiple content
// processes, but it'll do for now.
aWriter.StringProperty("name", "Content");
} else {
aWriter.StringProperty("name", Name());
}
#else
aWriter.StringProperty("name", Name());
#endif
aWriter.IntProperty("tid", static_cast<int>(mThreadId));
aWriter.StartObjectProperty("samples");
{
{
JSONSchemaWriter schema(aWriter);
schema.WriteField("stack");
schema.WriteField("time");
schema.WriteField("responsiveness");
schema.WriteField("rss");
schema.WriteField("uss");
schema.WriteField("frameNumber");
schema.WriteField("power");
}
aWriter.StartArrayProperty("data");
{
if (mSavedStreamedSamples) {
// We would only have saved streamed samples during shutdown
// streaming, which cares about dumping the entire buffer, and thus
// should have passed in 0 for aSinceTime.
MOZ_ASSERT(aSinceTime == 0);
aWriter.Splice(mSavedStreamedSamples.get());
mSavedStreamedSamples.reset();
}
mBuffer->StreamSamplesToJSON(aWriter, mThreadId, aSinceTime,
#ifndef SPS_STANDALONE
mPseudoStack->mContext,
#else
nullptr,
#endif
aUniqueStacks);
}
aWriter.EndArray();
}
aWriter.EndObject();
aWriter.StartObjectProperty("markers");
{
{
JSONSchemaWriter schema(aWriter);
schema.WriteField("name");
schema.WriteField("time");
schema.WriteField("data");
}
aWriter.StartArrayProperty("data");
{
if (mSavedStreamedMarkers) {
MOZ_ASSERT(aSinceTime == 0);
aWriter.Splice(mSavedStreamedMarkers.get());
mSavedStreamedMarkers.reset();
}
mBuffer->StreamMarkersToJSON(aWriter, mThreadId, aSinceTime, aUniqueStacks);
}
aWriter.EndArray();
}
aWriter.EndObject();
}
void ThreadProfile::FlushSamplesAndMarkers()
{
// This function is used to serialize the current buffer just before
// JSContext destruction.
MOZ_ASSERT(mPseudoStack->mContext);
// Unlike StreamJSObject, do not surround the samples in brackets by calling
// aWriter.{Start,End}BareList. The result string will be a comma-separated
// list of JSON object literals that will prepended by StreamJSObject into
// an existing array.
//
// Note that the UniqueStacks instance is persisted so that the frame-index
// mapping is stable across JS shutdown.
#ifndef SPS_STANDALONE
mUniqueStacks.emplace(mPseudoStack->mContext);
#else
mUniqueStacks.emplace(nullptr);
#endif
{
SpliceableChunkedJSONWriter b;
b.StartBareList();
{
mBuffer->StreamSamplesToJSON(b, mThreadId, /* aSinceTime = */ 0,
#ifndef SPS_STANDALONE
mPseudoStack->mContext,
#else
nullptr,
#endif
*mUniqueStacks);
}
b.EndBareList();
mSavedStreamedSamples = b.WriteFunc()->CopyData();
}
{
SpliceableChunkedJSONWriter b;
b.StartBareList();
{
mBuffer->StreamMarkersToJSON(b, mThreadId, /* aSinceTime = */ 0, *mUniqueStacks);
}
b.EndBareList();
mSavedStreamedMarkers = b.WriteFunc()->CopyData();
}
// Reset the buffer. Attempting to symbolicate JS samples after mContext has
// gone away will crash.
mBuffer->reset();
}
PseudoStack* ThreadProfile::GetPseudoStack()
{
return mPseudoStack;
}
void ThreadProfile::BeginUnwind()
{
mMutex->Lock();
}
void ThreadProfile::EndUnwind()
{
mMutex->Unlock();
}
::Mutex& ThreadProfile::GetMutex()
{
return *mMutex.get();
}
void ThreadProfile::DuplicateLastSample()
{
mBuffer->DuplicateLastSample(mThreadId);
}

View file

@ -1,107 +0,0 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef MOZ_THREAD_PROFILE_H
#define MOZ_THREAD_PROFILE_H
#include "ProfileBuffer.h"
#include "ThreadInfo.h"
class ThreadProfile
{
public:
ThreadProfile(ThreadInfo* aThreadInfo, ProfileBuffer* aBuffer);
virtual ~ThreadProfile();
void addTag(const ProfileEntry& aTag);
/**
* Track a marker which has been inserted into the ThreadProfile.
* This marker can safely be deleted once the generation has
* expired.
*/
void addStoredMarker(ProfilerMarker *aStoredMarker);
PseudoStack* GetPseudoStack();
::Mutex& GetMutex();
void StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime = 0);
/**
* Call this method when the JS entries inside the buffer are about to
* become invalid, i.e., just before JS shutdown.
*/
void FlushSamplesAndMarkers();
void BeginUnwind();
virtual void EndUnwind();
virtual SyncProfile* AsSyncProfile() { return nullptr; }
bool IsMainThread() const { return mIsMainThread; }
const char* Name() const { return mThreadInfo->Name(); }
int ThreadId() const { return mThreadId; }
PlatformData* GetPlatformData() const { return mPlatformData; }
void* GetStackTop() const { return mStackTop; }
void DuplicateLastSample();
ThreadInfo* GetThreadInfo() const { return mThreadInfo; }
#ifndef SPS_STANDALONE
ThreadResponsiveness* GetThreadResponsiveness() { return &mRespInfo; }
#endif
bool CanInvokeJS() const { return mThreadInfo->CanInvokeJS(); }
void SetPendingDelete()
{
mPseudoStack = nullptr;
mPlatformData = nullptr;
}
uint32_t bufferGeneration() const {
return mBuffer->mGeneration;
}
protected:
void StreamSamplesAndMarkers(SpliceableJSONWriter& aWriter, double aSinceTime,
UniqueStacks& aUniqueStacks);
private:
FRIEND_TEST(ThreadProfile, InsertOneTag);
FRIEND_TEST(ThreadProfile, InsertOneTagWithTinyBuffer);
FRIEND_TEST(ThreadProfile, InsertTagsNoWrap);
FRIEND_TEST(ThreadProfile, InsertTagsWrap);
FRIEND_TEST(ThreadProfile, MemoryMeasure);
ThreadInfo* mThreadInfo;
const RefPtr<ProfileBuffer> mBuffer;
// JS frames in the buffer may require a live JSRuntime to stream (e.g.,
// stringifying JIT frames). In the case of JSRuntime destruction,
// FlushSamplesAndMarkers should be called to save them. These are spliced
// into the final stream.
mozilla::UniquePtr<char[]> mSavedStreamedSamples;
mozilla::UniquePtr<char[]> mSavedStreamedMarkers;
mozilla::Maybe<UniqueStacks> mUniqueStacks;
PseudoStack* mPseudoStack;
mozilla::UniquePtr<Mutex> mMutex;
int mThreadId;
bool mIsMainThread;
PlatformData* mPlatformData; // Platform specific data.
void* const mStackTop;
#ifndef SPS_STANDALONE
ThreadResponsiveness mRespInfo;
#endif
// Only Linux is using a signal sender, instead of stopping the thread, so we
// need some space to store the data which cannot be collected in the signal
// handler code.
#ifdef XP_LINUX
public:
int64_t mRssMemory;
int64_t mUssMemory;
#endif
};
#endif

View file

@ -1,715 +0,0 @@
// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in
// the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google, Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
// OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
/*
# vim: sw=2
*/
#include <stdio.h>
#include <math.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/prctl.h> // set name
#include <stdlib.h>
#include <sched.h>
#ifdef ANDROID
#include <android/log.h>
#else
#define __android_log_print(a, ...)
#endif
#include <ucontext.h>
// Ubuntu Dapper requires memory pages to be marked as
// executable. Otherwise, OS raises an exception when executing code
// in that page.
#include <sys/types.h> // mmap & munmap
#include <sys/mman.h> // mmap & munmap
#include <sys/stat.h> // open
#include <fcntl.h> // open
#include <unistd.h> // sysconf
#include <semaphore.h>
#ifdef __GLIBC__
#include <execinfo.h> // backtrace, backtrace_symbols
#endif // def __GLIBC__
#include <strings.h> // index
#include <errno.h>
#include <stdarg.h>
#include "prenv.h"
#include "platform.h"
#include "GeckoProfiler.h"
#include "mozilla/Mutex.h"
#include "mozilla/Atomics.h"
#include "mozilla/LinuxSignal.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/DebugOnly.h"
#include "ProfileEntry.h"
#include "nsThreadUtils.h"
#include "GeckoSampler.h"
#include "ThreadResponsiveness.h"
#if defined(__ARM_EABI__) && defined(ANDROID)
// Should also work on other Android and ARM Linux, but not tested there yet.
# define USE_EHABI_STACKWALK
# include "EHABIStackWalk.h"
#elif defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_x86_linux)
# define USE_LUL_STACKWALK
# include "lul/LulMain.h"
# include "lul/platform-linux-lul.h"
#endif
// Memory profile
#include "nsMemoryReporterManager.h"
#include <string.h>
#include <list>
#define SIGNAL_SAVE_PROFILE SIGUSR2
using namespace mozilla;
#if defined(USE_LUL_STACKWALK)
// A singleton instance of the library. It is initialised at first
// use. Currently only the main thread can call Sampler::Start, so
// there is no need for a mechanism to ensure that it is only
// created once in a multi-thread-use situation.
lul::LUL* sLUL = nullptr;
// This is the sLUL initialization routine.
static void sLUL_initialization_routine(void)
{
MOZ_ASSERT(!sLUL);
MOZ_ASSERT(gettid() == getpid()); /* "this is the main thread" */
sLUL = new lul::LUL(logging_sink_for_LUL);
// Read all the unwind info currently available.
read_procmaps(sLUL);
}
#endif
/* static */ Thread::tid_t
Thread::GetCurrentId()
{
return gettid();
}
#if !defined(ANDROID)
// Keep track of when any of our threads calls fork(), so we can
// temporarily disable signal delivery during the fork() call. Not
// doing so appears to cause a kind of race, in which signals keep
// getting delivered to the thread doing fork(), which keeps causing
// it to fail and be restarted; hence forward progress is delayed a
// great deal. A side effect of this is to permanently disable
// sampling in the child process. See bug 837390.
// Unfortunately this is only doable on non-Android, since Bionic
// doesn't have pthread_atfork.
// This records the current state at the time we paused it.
static bool was_paused = false;
// In the parent, just before the fork, record the pausedness state,
// and then pause.
static void paf_prepare(void) {
if (Sampler::GetActiveSampler()) {
was_paused = Sampler::GetActiveSampler()->IsPaused();
Sampler::GetActiveSampler()->SetPaused(true);
} else {
was_paused = false;
}
}
// In the parent, just after the fork, return pausedness to the
// pre-fork state.
static void paf_parent(void) {
if (Sampler::GetActiveSampler())
Sampler::GetActiveSampler()->SetPaused(was_paused);
}
// Set up the fork handlers.
static void* setup_atfork() {
pthread_atfork(paf_prepare, paf_parent, NULL);
return NULL;
}
#endif /* !defined(ANDROID) */
struct SamplerRegistry {
static void AddActiveSampler(Sampler *sampler) {
ASSERT(!SamplerRegistry::sampler);
SamplerRegistry::sampler = sampler;
}
static void RemoveActiveSampler(Sampler *sampler) {
SamplerRegistry::sampler = NULL;
}
static Sampler *sampler;
};
Sampler *SamplerRegistry::sampler = NULL;
static mozilla::Atomic<ThreadProfile*> sCurrentThreadProfile;
static sem_t sSignalHandlingDone;
static void ProfilerSaveSignalHandler(int signal, siginfo_t* info, void* context) {
Sampler::GetActiveSampler()->RequestSave();
}
static void SetSampleContext(TickSample* sample, void* context)
{
// Extracting the sample from the context is extremely machine dependent.
ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
mcontext_t& mcontext = ucontext->uc_mcontext;
#if V8_HOST_ARCH_IA32
sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]);
sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]);
sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_EBP]);
#elif V8_HOST_ARCH_X64
sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_RIP]);
sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_RSP]);
sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_RBP]);
#elif V8_HOST_ARCH_ARM
// An undefined macro evaluates to 0, so this applies to Android's Bionic also.
#if !defined(ANDROID) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3))
sample->pc = reinterpret_cast<Address>(mcontext.gregs[R15]);
sample->sp = reinterpret_cast<Address>(mcontext.gregs[R13]);
sample->fp = reinterpret_cast<Address>(mcontext.gregs[R11]);
#ifdef ENABLE_ARM_LR_SAVING
sample->lr = reinterpret_cast<Address>(mcontext.gregs[R14]);
#endif
#else
sample->pc = reinterpret_cast<Address>(mcontext.arm_pc);
sample->sp = reinterpret_cast<Address>(mcontext.arm_sp);
sample->fp = reinterpret_cast<Address>(mcontext.arm_fp);
#ifdef ENABLE_ARM_LR_SAVING
sample->lr = reinterpret_cast<Address>(mcontext.arm_lr);
#endif
#endif
#elif V8_HOST_ARCH_MIPS
// Implement this on MIPS.
UNIMPLEMENTED();
#endif
}
#ifdef ANDROID
#define V8_HOST_ARCH_ARM 1
#define SYS_gettid __NR_gettid
#define SYS_tgkill __NR_tgkill
#else
#define V8_HOST_ARCH_X64 1
#endif
namespace {
void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
// Avoid TSan warning about clobbering errno.
int savedErrno = errno;
if (!Sampler::GetActiveSampler()) {
sem_post(&sSignalHandlingDone);
errno = savedErrno;
return;
}
TickSample sample_obj;
TickSample* sample = &sample_obj;
sample->context = context;
// If profiling, we extract the current pc and sp.
if (Sampler::GetActiveSampler()->IsProfiling()) {
SetSampleContext(sample, context);
}
sample->threadProfile = sCurrentThreadProfile;
sample->timestamp = mozilla::TimeStamp::Now();
sample->rssMemory = sample->threadProfile->mRssMemory;
sample->ussMemory = sample->threadProfile->mUssMemory;
Sampler::GetActiveSampler()->Tick(sample);
sCurrentThreadProfile = NULL;
sem_post(&sSignalHandlingDone);
errno = savedErrno;
}
} // namespace
static void ProfilerSignalThread(ThreadProfile *profile,
bool isFirstProfiledThread)
{
if (isFirstProfiledThread && Sampler::GetActiveSampler()->ProfileMemory()) {
profile->mRssMemory = nsMemoryReporterManager::ResidentFast();
profile->mUssMemory = nsMemoryReporterManager::ResidentUnique();
} else {
profile->mRssMemory = 0;
profile->mUssMemory = 0;
}
}
int tgkill(pid_t tgid, pid_t tid, int signalno) {
return syscall(SYS_tgkill, tgid, tid, signalno);
}
class PlatformData {
public:
PlatformData()
{
MOZ_COUNT_CTOR(PlatformData);
}
~PlatformData()
{
MOZ_COUNT_DTOR(PlatformData);
}
};
/* static */ PlatformData*
Sampler::AllocPlatformData(int aThreadId)
{
return new PlatformData;
}
/* static */ void
Sampler::FreePlatformData(PlatformData* aData)
{
delete aData;
}
static void* SignalSender(void* arg) {
// Taken from platform_thread_posix.cc
prctl(PR_SET_NAME, "SamplerThread", 0, 0, 0);
int vm_tgid_ = getpid();
DebugOnly<int> my_tid = gettid();
unsigned int nSignalsSent = 0;
TimeDuration lastSleepOverhead = 0;
TimeStamp sampleStart = TimeStamp::Now();
while (SamplerRegistry::sampler->IsActive()) {
SamplerRegistry::sampler->HandleSaveRequest();
SamplerRegistry::sampler->DeleteExpiredMarkers();
if (!SamplerRegistry::sampler->IsPaused()) {
::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
std::vector<ThreadInfo*> threads =
SamplerRegistry::sampler->GetRegisteredThreads();
bool isFirstProfiledThread = true;
for (uint32_t i = 0; i < threads.size(); i++) {
ThreadInfo* info = threads[i];
// This will be null if we're not interested in profiling this thread.
if (!info->Profile() || info->IsPendingDelete())
continue;
PseudoStack::SleepState sleeping = info->Stack()->observeSleeping();
if (sleeping == PseudoStack::SLEEPING_AGAIN) {
info->Profile()->DuplicateLastSample();
continue;
}
info->Profile()->GetThreadResponsiveness()->Update();
// We use sCurrentThreadProfile the ThreadProfile for the
// thread we're profiling to the signal handler
sCurrentThreadProfile = info->Profile();
int threadId = info->ThreadId();
MOZ_ASSERT(threadId != my_tid);
// Profile from the signal sender for information which is not signal
// safe, and will have low variation between the emission of the signal
// and the signal handler catch.
ProfilerSignalThread(sCurrentThreadProfile, isFirstProfiledThread);
// Profile from the signal handler for information which is signal safe
// and needs to be precise too, such as the stack of the interrupted
// thread.
if (tgkill(vm_tgid_, threadId, SIGPROF) != 0) {
printf_stderr("profiler failed to signal tid=%d\n", threadId);
#ifdef DEBUG
abort();
#else
continue;
#endif
}
// Wait for the signal handler to run before moving on to the next one
sem_wait(&sSignalHandlingDone);
isFirstProfiledThread = false;
// The LUL unwind object accumulates frame statistics.
// Periodically we should poke it to give it a chance to print
// those statistics. This involves doing I/O (fprintf,
// __android_log_print, etc) and so can't safely be done from
// the unwinder threads, which is why it is done here.
if ((++nSignalsSent & 0xF) == 0) {
# if defined(USE_LUL_STACKWALK)
sLUL->MaybeShowStats();
# endif
}
}
}
TimeStamp targetSleepEndTime = sampleStart + TimeDuration::FromMicroseconds(SamplerRegistry::sampler->interval() * 1000);
TimeStamp beforeSleep = TimeStamp::Now();
TimeDuration targetSleepDuration = targetSleepEndTime - beforeSleep;
double sleepTime = std::max(0.0, (targetSleepDuration - lastSleepOverhead).ToMicroseconds());
OS::SleepMicro(sleepTime);
sampleStart = TimeStamp::Now();
lastSleepOverhead = sampleStart - (beforeSleep + TimeDuration::FromMicroseconds(sleepTime));
}
return 0;
}
Sampler::Sampler(double interval, bool profiling, int entrySize)
: interval_(interval),
profiling_(profiling),
paused_(false),
active_(false),
entrySize_(entrySize) {
MOZ_COUNT_CTOR(Sampler);
}
Sampler::~Sampler() {
MOZ_COUNT_DTOR(Sampler);
ASSERT(!signal_sender_launched_);
}
void Sampler::Start() {
LOG("Sampler started");
#if defined(USE_EHABI_STACKWALK)
mozilla::EHABIStackWalkInit();
#elif defined(USE_LUL_STACKWALK)
// NOTE: this isn't thread-safe. But we expect Sampler::Start to be
// called only from the main thread, so this is OK in general.
if (!sLUL) {
sLUL_initialization_routine();
}
#endif
SamplerRegistry::AddActiveSampler(this);
// Initialize signal handler communication
sCurrentThreadProfile = NULL;
if (sem_init(&sSignalHandlingDone, /* pshared: */ 0, /* value: */ 0) != 0) {
LOG("Error initializing semaphore");
return;
}
// Request profiling signals.
LOG("Request signal");
struct sigaction sa;
sa.sa_sigaction = MOZ_SIGNAL_TRAMPOLINE(ProfilerSignalHandler);
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_SIGINFO;
if (sigaction(SIGPROF, &sa, &old_sigprof_signal_handler_) != 0) {
LOG("Error installing signal");
return;
}
// Request save profile signals
struct sigaction sa2;
sa2.sa_sigaction = ProfilerSaveSignalHandler;
sigemptyset(&sa2.sa_mask);
sa2.sa_flags = SA_RESTART | SA_SIGINFO;
if (sigaction(SIGNAL_SAVE_PROFILE, &sa2, &old_sigsave_signal_handler_) != 0) {
LOG("Error installing start signal");
return;
}
LOG("Signal installed");
signal_handler_installed_ = true;
#if defined(USE_LUL_STACKWALK)
// Switch into unwind mode. After this point, we can't add or
// remove any unwind info to/from this LUL instance. The only thing
// we can do with it is Unwind() calls.
sLUL->EnableUnwinding();
// Has a test been requested?
if (PR_GetEnv("MOZ_PROFILER_LUL_TEST")) {
int nTests = 0, nTestsPassed = 0;
RunLulUnitTests(&nTests, &nTestsPassed, sLUL);
}
#endif
// Start a thread that sends SIGPROF signal to VM thread.
// Sending the signal ourselves instead of relying on itimer provides
// much better accuracy.
SetActive(true);
if (pthread_create(
&signal_sender_thread_, NULL, SignalSender, NULL) == 0) {
signal_sender_launched_ = true;
}
LOG("Profiler thread started");
}
void Sampler::Stop() {
SetActive(false);
// Wait for signal sender termination (it will exit after setting
// active_ to false).
if (signal_sender_launched_) {
pthread_join(signal_sender_thread_, NULL);
signal_sender_launched_ = false;
}
SamplerRegistry::RemoveActiveSampler(this);
// Restore old signal handler
if (signal_handler_installed_) {
sigaction(SIGNAL_SAVE_PROFILE, &old_sigsave_signal_handler_, 0);
sigaction(SIGPROF, &old_sigprof_signal_handler_, 0);
signal_handler_installed_ = false;
}
}
bool Sampler::RegisterCurrentThread(const char* aName,
PseudoStack* aPseudoStack,
bool aIsMainThread, void* stackTop)
{
if (!Sampler::sRegisteredThreadsMutex)
return false;
::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
int id = gettid();
for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
ThreadInfo* info = sRegisteredThreads->at(i);
if (info->ThreadId() == id && !info->IsPendingDelete()) {
// Thread already registered. This means the first unregister will be
// too early.
ASSERT(false);
return false;
}
}
set_tls_stack_top(stackTop);
ThreadInfo* info = new StackOwningThreadInfo(aName, id,
aIsMainThread, aPseudoStack, stackTop);
if (sActiveSampler) {
sActiveSampler->RegisterThread(info);
}
sRegisteredThreads->push_back(info);
return true;
}
void Sampler::UnregisterCurrentThread()
{
if (!Sampler::sRegisteredThreadsMutex)
return;
tlsStackTop.set(nullptr);
::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
int id = gettid();
for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
ThreadInfo* info = sRegisteredThreads->at(i);
if (info->ThreadId() == id && !info->IsPendingDelete()) {
if (profiler_is_active()) {
// We still want to show the results of this thread if you
// save the profile shortly after a thread is terminated.
// For now we will defer the delete to profile stop.
info->SetPendingDelete();
break;
} else {
delete info;
sRegisteredThreads->erase(sRegisteredThreads->begin() + i);
break;
}
}
}
}
#ifdef ANDROID
static struct sigaction old_sigstart_signal_handler;
const int SIGSTART = SIGUSR2;
static void freeArray(const char** array, int size) {
for (int i = 0; i < size; i++) {
free((void*) array[i]);
}
}
static uint32_t readCSVArray(char* csvList, const char** buffer) {
uint32_t count;
char* savePtr;
int newlinePos = strlen(csvList) - 1;
if (csvList[newlinePos] == '\n') {
csvList[newlinePos] = '\0';
}
char* item = strtok_r(csvList, ",", &savePtr);
for (count = 0; item; item = strtok_r(NULL, ",", &savePtr)) {
int length = strlen(item) + 1; // Include \0
char* newBuf = (char*) malloc(sizeof(char) * length);
buffer[count] = newBuf;
strncpy(newBuf, item, length);
count++;
}
return count;
}
// Currently support only the env variables
// reported in read_profiler_env
static void ReadProfilerVars(const char* fileName, const char** features,
uint32_t* featureCount, const char** threadNames, uint32_t* threadCount) {
FILE* file = fopen(fileName, "r");
const int bufferSize = 1024;
char line[bufferSize];
char* feature;
char* value;
char* savePtr;
if (file) {
while (fgets(line, bufferSize, file) != NULL) {
feature = strtok_r(line, "=", &savePtr);
value = strtok_r(NULL, "", &savePtr);
if (strncmp(feature, PROFILER_INTERVAL, bufferSize) == 0) {
set_profiler_interval(value);
} else if (strncmp(feature, PROFILER_ENTRIES, bufferSize) == 0) {
set_profiler_entries(value);
} else if (strncmp(feature, PROFILER_STACK, bufferSize) == 0) {
set_profiler_scan(value);
} else if (strncmp(feature, PROFILER_FEATURES, bufferSize) == 0) {
*featureCount = readCSVArray(value, features);
} else if (strncmp(feature, "threads", bufferSize) == 0) {
*threadCount = readCSVArray(value, threadNames);
}
}
fclose(file);
}
}
static void DoStartTask() {
uint32_t featureCount = 0;
uint32_t threadCount = 0;
// Just allocate 10 features for now
// FIXME: these don't really point to const chars*
// So we free them later, but we don't want to change the const char**
// declaration in profiler_start. Annoying but ok for now.
const char* threadNames[10];
const char* features[10];
const char* profilerConfigFile = "/data/local/tmp/profiler.options";
ReadProfilerVars(profilerConfigFile, features, &featureCount, threadNames, &threadCount);
MOZ_ASSERT(featureCount < 10);
MOZ_ASSERT(threadCount < 10);
profiler_start(PROFILE_DEFAULT_ENTRY, 1,
features, featureCount,
threadNames, threadCount);
freeArray(threadNames, threadCount);
freeArray(features, featureCount);
}
static void StartSignalHandler(int signal, siginfo_t* info, void* context) {
class StartTask : public Runnable {
public:
NS_IMETHOD Run() override {
DoStartTask();
return NS_OK;
}
};
// XXX: technically NS_DispatchToMainThread is NOT async signal safe. We risk
// nasty things like deadlocks, but the probability is very low and we
// typically only do this once so it tends to be ok. See bug 909403.
NS_DispatchToMainThread(new StartTask());
}
void OS::Startup()
{
LOG("Registering start signal");
struct sigaction sa;
sa.sa_sigaction = StartSignalHandler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_SIGINFO;
if (sigaction(SIGSTART, &sa, &old_sigstart_signal_handler) != 0) {
LOG("Error installing signal");
}
}
#else
void OS::Startup() {
// Set up the fork handlers.
setup_atfork();
}
#endif
void TickSample::PopulateContext(void* aContext)
{
MOZ_ASSERT(aContext);
ucontext_t* pContext = reinterpret_cast<ucontext_t*>(aContext);
if (!getcontext(pContext)) {
context = pContext;
SetSampleContext(this, aContext);
}
}
void OS::SleepMicro(int microseconds)
{
if (MOZ_UNLIKELY(microseconds >= 1000000)) {
// Use usleep for larger intervals, because the nanosleep
// code below only supports intervals < 1 second.
MOZ_ALWAYS_TRUE(!::usleep(microseconds));
return;
}
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = microseconds * 1000UL;
int rv = ::nanosleep(&ts, &ts);
while (rv != 0 && errno == EINTR) {
// Keep waiting in case of interrupt.
// nanosleep puts the remaining time back into ts.
rv = ::nanosleep(&ts, &ts);
}
MOZ_ASSERT(!rv, "nanosleep call failed");
}

View file

@ -1,469 +0,0 @@
/* 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 <dlfcn.h>
#include <unistd.h>
#include <sys/mman.h>
#include <mach/mach_init.h>
#include <mach-o/dyld.h>
#include <mach-o/getsect.h>
#include <AvailabilityMacros.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <libkern/OSAtomic.h>
#include <mach/mach.h>
#include <mach/semaphore.h>
#include <mach/task.h>
#include <mach/vm_statistics.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#ifndef SPS_STANDALONE
#include "ThreadResponsiveness.h"
#include "nsThreadUtils.h"
// Memory profile
#include "nsMemoryReporterManager.h"
#endif
#include "platform.h"
#include "GeckoSampler.h"
#include "mozilla/TimeStamp.h"
using mozilla::TimeStamp;
using mozilla::TimeDuration;
// this port is based off of v8 svn revision 9837
// XXX: this is a very stubbed out implementation
// that only supports a single Sampler
struct SamplerRegistry {
static void AddActiveSampler(Sampler *sampler) {
ASSERT(!SamplerRegistry::sampler);
SamplerRegistry::sampler = sampler;
}
static void RemoveActiveSampler(Sampler *sampler) {
SamplerRegistry::sampler = NULL;
}
static Sampler *sampler;
};
Sampler *SamplerRegistry::sampler = NULL;
#ifdef DEBUG
// 0 is never a valid thread id on MacOSX since a pthread_t is a pointer.
static const pthread_t kNoThread = (pthread_t) 0;
#endif
void OS::Startup() {
}
void OS::Sleep(int milliseconds) {
usleep(1000 * milliseconds);
}
void OS::SleepMicro(int microseconds) {
usleep(microseconds);
}
Thread::Thread(const char* name)
: stack_size_(0) {
set_name(name);
}
Thread::~Thread() {
}
static void SetThreadName(const char* name) {
// pthread_setname_np is only available in 10.6 or later, so test
// for it at runtime.
int (*dynamic_pthread_setname_np)(const char*);
*reinterpret_cast<void**>(&dynamic_pthread_setname_np) =
dlsym(RTLD_DEFAULT, "pthread_setname_np");
if (!dynamic_pthread_setname_np)
return;
// Mac OS X does not expose the length limit of the name, so hardcode it.
static const int kMaxNameLength = 63;
USE(kMaxNameLength);
ASSERT(Thread::kMaxThreadNameLength <= kMaxNameLength);
dynamic_pthread_setname_np(name);
}
static void* ThreadEntry(void* arg) {
Thread* thread = reinterpret_cast<Thread*>(arg);
thread->thread_ = pthread_self();
SetThreadName(thread->name());
ASSERT(thread->thread_ != kNoThread);
thread->Run();
return NULL;
}
void Thread::set_name(const char* name) {
strncpy(name_, name, sizeof(name_));
name_[sizeof(name_) - 1] = '\0';
}
void Thread::Start() {
pthread_attr_t* attr_ptr = NULL;
pthread_attr_t attr;
if (stack_size_ > 0) {
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size_));
attr_ptr = &attr;
}
pthread_create(&thread_, attr_ptr, ThreadEntry, this);
ASSERT(thread_ != kNoThread);
}
void Thread::Join() {
pthread_join(thread_, NULL);
}
class PlatformData {
public:
PlatformData() : profiled_thread_(mach_thread_self())
{
profiled_pthread_ = pthread_from_mach_thread_np(profiled_thread_);
}
~PlatformData() {
// Deallocate Mach port for thread.
mach_port_deallocate(mach_task_self(), profiled_thread_);
}
thread_act_t profiled_thread() { return profiled_thread_; }
pthread_t profiled_pthread() { return profiled_pthread_; }
private:
// Note: for profiled_thread_ Mach primitives are used instead of PThread's
// because the latter doesn't provide thread manipulation primitives required.
// For details, consult "Mac OS X Internals" book, Section 7.3.
thread_act_t profiled_thread_;
// we also store the pthread because Mach threads have no concept of stack
// and we want to be able to get the stack size when we need to unwind the
// stack using frame pointers.
pthread_t profiled_pthread_;
};
/* static */ PlatformData*
Sampler::AllocPlatformData(int aThreadId)
{
return new PlatformData;
}
/* static */ void
Sampler::FreePlatformData(PlatformData* aData)
{
delete aData;
}
class SamplerThread : public Thread {
public:
explicit SamplerThread(double interval)
: Thread("SamplerThread")
, intervalMicro_(floor(interval * 1000 + 0.5))
{
if (intervalMicro_ <= 0) {
intervalMicro_ = 1;
}
}
static void AddActiveSampler(Sampler* sampler) {
SamplerRegistry::AddActiveSampler(sampler);
if (instance_ == NULL) {
instance_ = new SamplerThread(sampler->interval());
instance_->Start();
}
}
static void RemoveActiveSampler(Sampler* sampler) {
instance_->Join();
//XXX: unlike v8 we need to remove the active sampler after doing the Join
// because we drop the sampler immediately
SamplerRegistry::RemoveActiveSampler(sampler);
delete instance_;
instance_ = NULL;
}
// Implement Thread::Run().
virtual void Run() {
TimeDuration lastSleepOverhead = 0;
TimeStamp sampleStart = TimeStamp::Now();
while (SamplerRegistry::sampler->IsActive()) {
SamplerRegistry::sampler->DeleteExpiredMarkers();
if (!SamplerRegistry::sampler->IsPaused()) {
::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
std::vector<ThreadInfo*> threads =
SamplerRegistry::sampler->GetRegisteredThreads();
bool isFirstProfiledThread = true;
for (uint32_t i = 0; i < threads.size(); i++) {
ThreadInfo* info = threads[i];
// This will be null if we're not interested in profiling this thread.
if (!info->Profile() || info->IsPendingDelete())
continue;
PseudoStack::SleepState sleeping = info->Stack()->observeSleeping();
if (sleeping == PseudoStack::SLEEPING_AGAIN) {
info->Profile()->DuplicateLastSample();
continue;
}
#ifndef SPS_STANDALONE
info->Profile()->GetThreadResponsiveness()->Update();
#endif
ThreadProfile* thread_profile = info->Profile();
SampleContext(SamplerRegistry::sampler, thread_profile,
isFirstProfiledThread);
isFirstProfiledThread = false;
}
}
TimeStamp targetSleepEndTime = sampleStart + TimeDuration::FromMicroseconds(intervalMicro_);
TimeStamp beforeSleep = TimeStamp::Now();
TimeDuration targetSleepDuration = targetSleepEndTime - beforeSleep;
double sleepTime = std::max(0.0, (targetSleepDuration - lastSleepOverhead).ToMicroseconds());
OS::SleepMicro(sleepTime);
sampleStart = TimeStamp::Now();
lastSleepOverhead = sampleStart - (beforeSleep + TimeDuration::FromMicroseconds(sleepTime));
}
}
void SampleContext(Sampler* sampler, ThreadProfile* thread_profile,
bool isFirstProfiledThread)
{
thread_act_t profiled_thread =
thread_profile->GetPlatformData()->profiled_thread();
TickSample sample_obj;
TickSample* sample = &sample_obj;
// Unique Set Size is not supported on Mac.
sample->ussMemory = 0;
sample->rssMemory = 0;
#ifndef SPS_STANDALONE
if (isFirstProfiledThread && Sampler::GetActiveSampler()->ProfileMemory()) {
sample->rssMemory = nsMemoryReporterManager::ResidentFast();
}
#endif
// We're using thread_suspend on OS X because pthread_kill (which is what
// we're using on Linux) has less consistent performance and causes
// strange crashes, see bug 1166778 and bug 1166808.
if (KERN_SUCCESS != thread_suspend(profiled_thread)) return;
#if V8_HOST_ARCH_X64
thread_state_flavor_t flavor = x86_THREAD_STATE64;
x86_thread_state64_t state;
mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT;
#if __DARWIN_UNIX03
#define REGISTER_FIELD(name) __r ## name
#else
#define REGISTER_FIELD(name) r ## name
#endif // __DARWIN_UNIX03
#elif V8_HOST_ARCH_IA32
thread_state_flavor_t flavor = i386_THREAD_STATE;
i386_thread_state_t state;
mach_msg_type_number_t count = i386_THREAD_STATE_COUNT;
#if __DARWIN_UNIX03
#define REGISTER_FIELD(name) __e ## name
#else
#define REGISTER_FIELD(name) e ## name
#endif // __DARWIN_UNIX03
#else
#error Unsupported Mac OS X host architecture.
#endif // V8_HOST_ARCH
if (thread_get_state(profiled_thread,
flavor,
reinterpret_cast<natural_t*>(&state),
&count) == KERN_SUCCESS) {
sample->pc = reinterpret_cast<Address>(state.REGISTER_FIELD(ip));
sample->sp = reinterpret_cast<Address>(state.REGISTER_FIELD(sp));
sample->fp = reinterpret_cast<Address>(state.REGISTER_FIELD(bp));
sample->timestamp = mozilla::TimeStamp::Now();
sample->threadProfile = thread_profile;
sampler->Tick(sample);
}
thread_resume(profiled_thread);
}
int intervalMicro_;
//RuntimeProfilerRateLimiter rate_limiter_;
static SamplerThread* instance_;
DISALLOW_COPY_AND_ASSIGN(SamplerThread);
};
#undef REGISTER_FIELD
SamplerThread* SamplerThread::instance_ = NULL;
Sampler::Sampler(double interval, bool profiling, int entrySize)
: // isolate_(isolate),
interval_(interval),
profiling_(profiling),
paused_(false),
active_(false),
entrySize_(entrySize) /*,
samples_taken_(0)*/ {
}
Sampler::~Sampler() {
ASSERT(!IsActive());
}
void Sampler::Start() {
ASSERT(!IsActive());
SetActive(true);
SamplerThread::AddActiveSampler(this);
}
void Sampler::Stop() {
ASSERT(IsActive());
SetActive(false);
SamplerThread::RemoveActiveSampler(this);
}
pthread_t
Sampler::GetProfiledThread(PlatformData* aData)
{
return aData->profiled_pthread();
}
#include <sys/syscall.h>
pid_t gettid()
{
return (pid_t) syscall(SYS_thread_selfid);
}
/* static */ Thread::tid_t
Thread::GetCurrentId()
{
return gettid();
}
bool Sampler::RegisterCurrentThread(const char* aName,
PseudoStack* aPseudoStack,
bool aIsMainThread, void* stackTop)
{
if (!Sampler::sRegisteredThreadsMutex)
return false;
::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
int id = gettid();
for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
ThreadInfo* info = sRegisteredThreads->at(i);
if (info->ThreadId() == id && !info->IsPendingDelete()) {
// Thread already registered. This means the first unregister will be
// too early.
ASSERT(false);
return false;
}
}
set_tls_stack_top(stackTop);
ThreadInfo* info = new StackOwningThreadInfo(aName, id,
aIsMainThread, aPseudoStack, stackTop);
if (sActiveSampler) {
sActiveSampler->RegisterThread(info);
}
sRegisteredThreads->push_back(info);
return true;
}
void Sampler::UnregisterCurrentThread()
{
if (!Sampler::sRegisteredThreadsMutex)
return;
tlsStackTop.set(nullptr);
::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
int id = gettid();
for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
ThreadInfo* info = sRegisteredThreads->at(i);
if (info->ThreadId() == id && !info->IsPendingDelete()) {
if (profiler_is_active()) {
// We still want to show the results of this thread if you
// save the profile shortly after a thread is terminated.
// For now we will defer the delete to profile stop.
info->SetPendingDelete();
break;
} else {
delete info;
sRegisteredThreads->erase(sRegisteredThreads->begin() + i);
break;
}
}
}
}
void TickSample::PopulateContext(void* aContext)
{
// Note that this asm changes if PopulateContext's parameter list is altered
#if defined(SPS_PLAT_amd64_darwin)
asm (
// Compute caller's %rsp by adding to %rbp:
// 8 bytes for previous %rbp, 8 bytes for return address
"leaq 0x10(%%rbp), %0\n\t"
// Dereference %rbp to get previous %rbp
"movq (%%rbp), %1\n\t"
:
"=r"(sp),
"=r"(fp)
);
#elif defined(SPS_PLAT_x86_darwin)
asm (
// Compute caller's %esp by adding to %ebp:
// 4 bytes for aContext + 4 bytes for return address +
// 4 bytes for previous %ebp
"leal 0xc(%%ebp), %0\n\t"
// Dereference %ebp to get previous %ebp
"movl (%%ebp), %1\n\t"
:
"=r"(sp),
"=r"(fp)
);
#else
# error "Unsupported architecture"
#endif
pc = reinterpret_cast<Address>(__builtin_extract_return_addr(
__builtin_return_address(0)));
}

View file

@ -1,431 +0,0 @@
// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in
// the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google, Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
// OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
#include <windows.h>
#include <mmsystem.h>
#include <process.h>
#include "platform.h"
#include "GeckoSampler.h"
#include "ThreadResponsiveness.h"
#include "ProfileEntry.h"
// Memory profile
#include "nsMemoryReporterManager.h"
#include "mozilla/StackWalk_windows.h"
class PlatformData {
public:
// Get a handle to the calling thread. This is the thread that we are
// going to profile. We need to make a copy of the handle because we are
// going to use it in the sampler thread. Using GetThreadHandle() will
// not work in this case. We're using OpenThread because DuplicateHandle
// for some reason doesn't work in Chrome's sandbox.
PlatformData(int aThreadId) : profiled_thread_(OpenThread(THREAD_GET_CONTEXT |
THREAD_SUSPEND_RESUME |
THREAD_QUERY_INFORMATION,
false,
aThreadId)) {}
~PlatformData() {
if (profiled_thread_ != NULL) {
CloseHandle(profiled_thread_);
profiled_thread_ = NULL;
}
}
HANDLE profiled_thread() { return profiled_thread_; }
private:
HANDLE profiled_thread_;
};
/* static */ PlatformData*
Sampler::AllocPlatformData(int aThreadId)
{
return new PlatformData(aThreadId);
}
/* static */ void
Sampler::FreePlatformData(PlatformData* aData)
{
delete aData;
}
uintptr_t
Sampler::GetThreadHandle(PlatformData* aData)
{
return (uintptr_t) aData->profiled_thread();
}
class SamplerThread : public Thread {
public:
SamplerThread(double interval, Sampler* sampler)
: Thread("SamplerThread")
, sampler_(sampler)
, interval_(interval)
{
interval_ = floor(interval + 0.5);
if (interval_ <= 0) {
interval_ = 1;
}
}
static void StartSampler(Sampler* sampler) {
if (instance_ == NULL) {
instance_ = new SamplerThread(sampler->interval(), sampler);
instance_->Start();
} else {
ASSERT(instance_->interval_ == sampler->interval());
}
}
static void StopSampler() {
instance_->Join();
delete instance_;
instance_ = NULL;
}
// Implement Thread::Run().
virtual void Run() {
// By default we'll not adjust the timer resolution which tends to be around
// 16ms. However, if the requested interval is sufficiently low we'll try to
// adjust the resolution to match.
if (interval_ < 10)
::timeBeginPeriod(interval_);
while (sampler_->IsActive()) {
sampler_->DeleteExpiredMarkers();
if (!sampler_->IsPaused()) {
::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
std::vector<ThreadInfo*> threads =
sampler_->GetRegisteredThreads();
bool isFirstProfiledThread = true;
for (uint32_t i = 0; i < threads.size(); i++) {
ThreadInfo* info = threads[i];
// This will be null if we're not interested in profiling this thread.
if (!info->Profile() || info->IsPendingDelete())
continue;
PseudoStack::SleepState sleeping = info->Stack()->observeSleeping();
if (sleeping == PseudoStack::SLEEPING_AGAIN) {
info->Profile()->DuplicateLastSample();
continue;
}
info->Profile()->GetThreadResponsiveness()->Update();
ThreadProfile* thread_profile = info->Profile();
SampleContext(sampler_, thread_profile, isFirstProfiledThread);
isFirstProfiledThread = false;
}
}
OS::Sleep(interval_);
}
// disable any timer resolution changes we've made
if (interval_ < 10)
::timeEndPeriod(interval_);
}
void SampleContext(Sampler* sampler, ThreadProfile* thread_profile,
bool isFirstProfiledThread)
{
uintptr_t thread = Sampler::GetThreadHandle(
thread_profile->GetPlatformData());
HANDLE profiled_thread = reinterpret_cast<HANDLE>(thread);
if (profiled_thread == NULL)
return;
// Context used for sampling the register state of the profiled thread.
CONTEXT context;
memset(&context, 0, sizeof(context));
TickSample sample_obj;
TickSample* sample = &sample_obj;
// Grab the timestamp before pausing the thread, to avoid deadlocks.
sample->timestamp = mozilla::TimeStamp::Now();
sample->threadProfile = thread_profile;
if (isFirstProfiledThread && Sampler::GetActiveSampler()->ProfileMemory()) {
sample->rssMemory = nsMemoryReporterManager::ResidentFast();
} else {
sample->rssMemory = 0;
}
// Unique Set Size is not supported on Windows.
sample->ussMemory = 0;
static const DWORD kSuspendFailed = static_cast<DWORD>(-1);
if (SuspendThread(profiled_thread) == kSuspendFailed)
return;
// SuspendThread is asynchronous, so the thread may still be running.
// Call GetThreadContext first to ensure the thread is really suspended.
// See https://blogs.msdn.microsoft.com/oldnewthing/20150205-00/?p=44743.
// Using only CONTEXT_CONTROL is faster but on 64-bit it causes crashes in
// RtlVirtualUnwind (see bug 1120126) so we set all the flags.
#if V8_HOST_ARCH_X64
context.ContextFlags = CONTEXT_FULL;
#else
context.ContextFlags = CONTEXT_CONTROL;
#endif
if (!GetThreadContext(profiled_thread, &context)) {
ResumeThread(profiled_thread);
return;
}
// Threads that may invoke JS require extra attention. Since, on windows,
// the jits also need to modify the same dynamic function table that we need
// to get a stack trace, we have to be wary of that to avoid deadlock.
//
// When embedded in Gecko, for threads that aren't the main thread,
// CanInvokeJS consults an unlocked value in the nsIThread, so we must
// consult this after suspending the profiled thread to avoid racing
// against a value change.
if (thread_profile->CanInvokeJS()) {
if (!TryAcquireStackWalkWorkaroundLock()) {
ResumeThread(profiled_thread);
return;
}
// It is safe to immediately drop the lock. We only need to contend with
// the case in which the profiled thread held needed system resources.
// If the profiled thread had held those resources, the trylock would have
// failed. Anyone else who grabs those resources will continue to make
// progress, since those threads are not suspended. Because of this,
// we cannot deadlock with them, and should let them run as they please.
ReleaseStackWalkWorkaroundLock();
}
#if V8_HOST_ARCH_X64
sample->pc = reinterpret_cast<Address>(context.Rip);
sample->sp = reinterpret_cast<Address>(context.Rsp);
sample->fp = reinterpret_cast<Address>(context.Rbp);
#else
sample->pc = reinterpret_cast<Address>(context.Eip);
sample->sp = reinterpret_cast<Address>(context.Esp);
sample->fp = reinterpret_cast<Address>(context.Ebp);
#endif
sample->context = &context;
sampler->Tick(sample);
ResumeThread(profiled_thread);
}
Sampler* sampler_;
int interval_; // units: ms
// Protects the process wide state below.
static SamplerThread* instance_;
DISALLOW_COPY_AND_ASSIGN(SamplerThread);
};
SamplerThread* SamplerThread::instance_ = NULL;
Sampler::Sampler(double interval, bool profiling, int entrySize)
: interval_(interval),
profiling_(profiling),
paused_(false),
active_(false),
entrySize_(entrySize) {
}
Sampler::~Sampler() {
ASSERT(!IsActive());
}
void Sampler::Start() {
ASSERT(!IsActive());
SetActive(true);
SamplerThread::StartSampler(this);
}
void Sampler::Stop() {
ASSERT(IsActive());
SetActive(false);
SamplerThread::StopSampler();
}
static const HANDLE kNoThread = INVALID_HANDLE_VALUE;
static unsigned int __stdcall ThreadEntry(void* arg) {
Thread* thread = reinterpret_cast<Thread*>(arg);
thread->Run();
return 0;
}
// Initialize a Win32 thread object. The thread has an invalid thread
// handle until it is started.
Thread::Thread(const char* name)
: stack_size_(0) {
thread_ = kNoThread;
set_name(name);
}
void Thread::set_name(const char* name) {
strncpy(name_, name, sizeof(name_));
name_[sizeof(name_) - 1] = '\0';
}
// Close our own handle for the thread.
Thread::~Thread() {
if (thread_ != kNoThread) CloseHandle(thread_);
}
// Create a new thread. It is important to use _beginthreadex() instead of
// the Win32 function CreateThread(), because the CreateThread() does not
// initialize thread specific structures in the C runtime library.
void Thread::Start() {
thread_ = reinterpret_cast<HANDLE>(
_beginthreadex(NULL,
static_cast<unsigned>(stack_size_),
ThreadEntry,
this,
0,
(unsigned int*) &thread_id_));
}
// Wait for thread to terminate.
void Thread::Join() {
if (thread_id_ != GetCurrentId()) {
WaitForSingleObject(thread_, INFINITE);
}
}
/* static */ Thread::tid_t
Thread::GetCurrentId()
{
return GetCurrentThreadId();
}
void OS::Startup() {
}
void OS::Sleep(int milliseconds) {
::Sleep(milliseconds);
}
bool Sampler::RegisterCurrentThread(const char* aName,
PseudoStack* aPseudoStack,
bool aIsMainThread, void* stackTop)
{
if (!Sampler::sRegisteredThreadsMutex)
return false;
::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
int id = GetCurrentThreadId();
for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
ThreadInfo* info = sRegisteredThreads->at(i);
if (info->ThreadId() == id && !info->IsPendingDelete()) {
// Thread already registered. This means the first unregister will be
// too early.
ASSERT(false);
return false;
}
}
set_tls_stack_top(stackTop);
ThreadInfo* info = new StackOwningThreadInfo(aName, id,
aIsMainThread, aPseudoStack, stackTop);
if (sActiveSampler) {
sActiveSampler->RegisterThread(info);
}
sRegisteredThreads->push_back(info);
return true;
}
void Sampler::UnregisterCurrentThread()
{
if (!Sampler::sRegisteredThreadsMutex)
return;
tlsStackTop.set(nullptr);
::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
int id = GetCurrentThreadId();
for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
ThreadInfo* info = sRegisteredThreads->at(i);
if (info->ThreadId() == id && !info->IsPendingDelete()) {
if (profiler_is_active()) {
// We still want to show the results of this thread if you
// save the profile shortly after a thread is terminated.
// For now we will defer the delete to profile stop.
info->SetPendingDelete();
break;
} else {
delete info;
sRegisteredThreads->erase(sRegisteredThreads->begin() + i);
break;
}
}
}
}
void TickSample::PopulateContext(void* aContext)
{
MOZ_ASSERT(aContext);
CONTEXT* pContext = reinterpret_cast<CONTEXT*>(aContext);
context = pContext;
RtlCaptureContext(pContext);
#if defined(SPS_PLAT_amd64_windows)
pc = reinterpret_cast<Address>(pContext->Rip);
sp = reinterpret_cast<Address>(pContext->Rsp);
fp = reinterpret_cast<Address>(pContext->Rbp);
#elif defined(SPS_PLAT_x86_windows)
pc = reinterpret_cast<Address>(pContext->Eip);
sp = reinterpret_cast<Address>(pContext->Esp);
fp = reinterpret_cast<Address>(pContext->Ebp);
#endif
}

File diff suppressed because it is too large Load diff

View file

@ -1,428 +0,0 @@
// Copyright (c) 2006-2011 The Chromium Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in
// the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google, Inc. nor the names of its contributors
// may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
// OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
#ifndef TOOLS_PLATFORM_H_
#define TOOLS_PLATFORM_H_
#ifdef SPS_STANDALONE
#define MOZ_COUNT_CTOR(name)
#define MOZ_COUNT_DTOR(name)
#endif
#ifdef ANDROID
#include <android/log.h>
#else
#define __android_log_print(a, ...)
#endif
#ifdef XP_UNIX
#include <pthread.h>
#endif
#include <stdint.h>
#include <math.h>
#ifndef SPS_STANDALONE
#include "MainThreadUtils.h"
#include "mozilla/Mutex.h"
#include "ThreadResponsiveness.h"
#endif
#include "mozilla/TimeStamp.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Unused.h"
#include "PlatformMacros.h"
#include "v8-support.h"
#include <vector>
#include "StackTop.h"
// We need a definition of gettid(), but Linux libc implementations don't
// provide a wrapper for it (except for Bionic)
#if defined(__linux__)
#include <unistd.h>
#if !defined(__BIONIC__)
#include <sys/syscall.h>
static inline pid_t gettid()
{
return (pid_t) syscall(SYS_gettid);
}
#endif
#endif
#ifdef XP_WIN
#include <windows.h>
#endif
#define ASSERT(a) MOZ_ASSERT(a)
bool moz_profiler_verbose();
#ifdef ANDROID
# if defined(__arm__) || defined(__thumb__)
# define ENABLE_SPS_LEAF_DATA
# define ENABLE_ARM_LR_SAVING
# endif
# define LOG(text) \
do { if (moz_profiler_verbose()) \
__android_log_write(ANDROID_LOG_ERROR, "Profiler", text); \
} while (0)
# define LOGF(format, ...) \
do { if (moz_profiler_verbose()) \
__android_log_print(ANDROID_LOG_ERROR, "Profiler", format, \
__VA_ARGS__); \
} while (0)
#else
# define LOG(text) \
do { if (moz_profiler_verbose()) fprintf(stderr, "Profiler: %s\n", text); \
} while (0)
# define LOGF(format, ...) \
do { if (moz_profiler_verbose()) fprintf(stderr, "Profiler: " format \
"\n", __VA_ARGS__); \
} while (0)
#endif
#if defined(XP_MACOSX) || defined(XP_WIN) || defined(XP_LINUX)
#define ENABLE_SPS_LEAF_DATA
#endif
typedef int32_t Atomic32;
extern mozilla::TimeStamp sStartTime;
typedef uint8_t* Address;
// ----------------------------------------------------------------------------
// Mutex
//
// Mutexes are used for serializing access to non-reentrant sections of code.
// The implementations of mutex should allow for nested/recursive locking.
class Mutex {
public:
virtual ~Mutex() {}
// Locks the given mutex. If the mutex is currently unlocked, it becomes
// locked and owned by the calling thread, and immediately. If the mutex
// is already locked by another thread, suspends the calling thread until
// the mutex is unlocked.
virtual int Lock() = 0;
// Unlocks the given mutex. The mutex is assumed to be locked and owned by
// the calling thread on entrance.
virtual int Unlock() = 0;
};
class MutexAutoLock {
public:
explicit MutexAutoLock(::Mutex& aMutex)
: mMutex(&aMutex)
{
mMutex->Lock();
}
~MutexAutoLock() {
mMutex->Unlock();
}
private:
Mutex* mMutex;
};
// ----------------------------------------------------------------------------
// OS
//
// This class has static methods for the different platform specific
// functions. Add methods here to cope with differences between the
// supported platforms.
class OS {
public:
// Sleep for a number of milliseconds.
static void Sleep(const int milliseconds);
// Sleep for a number of microseconds.
static void SleepMicro(const int microseconds);
// Called on startup to initialize platform specific things
static void Startup();
static mozilla::UniquePtr< ::Mutex> CreateMutex(const char* aDesc);
private:
static const int msPerSecond = 1000;
};
// ----------------------------------------------------------------------------
// Thread
//
// Thread objects are used for creating and running threads. When the start()
// method is called the new thread starts running the run() method in the new
// thread. The Thread object should not be deallocated before the thread has
// terminated.
class Thread {
public:
// Create new thread.
explicit Thread(const char* name);
virtual ~Thread();
// Start new thread by calling the Run() method in the new thread.
void Start();
void Join();
inline const char* name() const {
return name_;
}
// Abstract method for run handler.
virtual void Run() = 0;
// The thread name length is limited to 16 based on Linux's implementation of
// prctl().
static const int kMaxThreadNameLength = 16;
#ifdef XP_WIN
HANDLE thread_;
typedef DWORD tid_t;
tid_t thread_id_;
#else
typedef ::pid_t tid_t;
#endif
#if defined(XP_MACOSX)
pthread_t thread_;
#endif
static tid_t GetCurrentId();
private:
void set_name(const char *name);
char name_[kMaxThreadNameLength];
int stack_size_;
DISALLOW_COPY_AND_ASSIGN(Thread);
};
// ----------------------------------------------------------------------------
// HAVE_NATIVE_UNWIND
//
// Pseudo backtraces are available on all platforms. Native
// backtraces are available only on selected platforms. Breakpad is
// the only supported native unwinder. HAVE_NATIVE_UNWIND is set at
// build time to indicate whether native unwinding is possible on this
// platform.
#undef HAVE_NATIVE_UNWIND
#if defined(MOZ_PROFILING) \
&& (defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_arm_android) \
|| (defined(MOZ_WIDGET_ANDROID) && defined(__arm__)) \
|| defined(SPS_PLAT_x86_linux) \
|| defined(SPS_OS_windows) \
|| defined(SPS_OS_darwin))
# define HAVE_NATIVE_UNWIND
#endif
/* Some values extracted at startup from environment variables, that
control the behaviour of the breakpad unwinder. */
extern const char* PROFILER_INTERVAL;
extern const char* PROFILER_ENTRIES;
extern const char* PROFILER_STACK;
extern const char* PROFILER_FEATURES;
void read_profiler_env_vars();
void profiler_usage();
// Helper methods to expose modifying profiler behavior
bool set_profiler_interval(const char*);
bool set_profiler_entries(const char*);
bool set_profiler_scan(const char*);
bool is_native_unwinding_avail();
void set_tls_stack_top(void* stackTop);
// ----------------------------------------------------------------------------
// Sampler
//
// A sampler periodically samples the state of the VM and optionally
// (if used for profiling) the program counter and stack pointer for
// the thread that created it.
struct PseudoStack;
class ThreadProfile;
// TickSample captures the information collected for each sample.
class TickSample {
public:
TickSample()
: pc(NULL)
, sp(NULL)
, fp(NULL)
#ifdef ENABLE_ARM_LR_SAVING
, lr(NULL)
#endif
, context(NULL)
, isSamplingCurrentThread(false)
, threadProfile(nullptr)
, rssMemory(0)
, ussMemory(0)
{}
void PopulateContext(void* aContext);
Address pc; // Instruction pointer.
Address sp; // Stack pointer.
Address fp; // Frame pointer.
#ifdef ENABLE_ARM_LR_SAVING
Address lr; // ARM link register
#endif
void* context; // The context from the signal handler, if available. On
// Win32 this may contain the windows thread context.
bool isSamplingCurrentThread;
ThreadProfile* threadProfile;
mozilla::TimeStamp timestamp;
int64_t rssMemory;
int64_t ussMemory;
};
class ThreadInfo;
class PlatformData;
class GeckoSampler;
class SyncProfile;
class Sampler {
public:
// Initialize sampler.
explicit Sampler(double interval, bool profiling, int entrySize);
virtual ~Sampler();
double interval() const { return interval_; }
// This method is called for each sampling period with the current
// program counter.
virtual void Tick(TickSample* sample) = 0;
// Immediately captures the calling thread's call stack and returns it.
virtual SyncProfile* GetBacktrace() = 0;
// Request a save from a signal handler
virtual void RequestSave() = 0;
// Process any outstanding request outside a signal handler.
virtual void HandleSaveRequest() = 0;
// Delete markers which are no longer part of the profile due to buffer wraparound.
virtual void DeleteExpiredMarkers() = 0;
// Start and stop sampler.
void Start();
void Stop();
// Is the sampler used for profiling?
bool IsProfiling() const { return profiling_; }
// Whether the sampler is running (that is, consumes resources).
bool IsActive() const { return active_; }
// Low overhead way to stop the sampler from ticking
bool IsPaused() const { return paused_; }
void SetPaused(bool value) { NoBarrier_Store(&paused_, value); }
virtual bool ProfileThreads() const = 0;
int EntrySize() { return entrySize_; }
// We can't new/delete the type safely without defining it
// (-Wdelete-incomplete). Use these Alloc/Free functions instead.
static PlatformData* AllocPlatformData(int aThreadId);
static void FreePlatformData(PlatformData*);
// If we move the backtracing code into the platform files we won't
// need to have these hacks
#ifdef XP_WIN
// xxxehsan sucky hack :(
static uintptr_t GetThreadHandle(PlatformData*);
#endif
#ifdef XP_MACOSX
static pthread_t GetProfiledThread(PlatformData*);
#endif
static std::vector<ThreadInfo*> GetRegisteredThreads() {
return *sRegisteredThreads;
}
static bool RegisterCurrentThread(const char* aName,
PseudoStack* aPseudoStack,
bool aIsMainThread, void* stackTop);
static void UnregisterCurrentThread();
static void Startup();
// Should only be called on shutdown
static void Shutdown();
static GeckoSampler* GetActiveSampler() { return sActiveSampler; }
static void SetActiveSampler(GeckoSampler* sampler) { sActiveSampler = sampler; }
static mozilla::UniquePtr<Mutex> sRegisteredThreadsMutex;
static bool CanNotifyObservers() {
#if defined(SPS_OS_android)
// Android ANR reporter uses the profiler off the main thread
return NS_IsMainThread();
#else
MOZ_ASSERT(NS_IsMainThread());
return true;
#endif
}
protected:
static std::vector<ThreadInfo*>* sRegisteredThreads;
static GeckoSampler* sActiveSampler;
private:
void SetActive(bool value) { NoBarrier_Store(&active_, value); }
const double interval_;
const bool profiling_;
Atomic32 paused_;
Atomic32 active_;
const int entrySize_;
// Refactor me!
#if defined(SPS_OS_linux) || defined(SPS_OS_android)
bool signal_handler_installed_;
struct sigaction old_sigprof_signal_handler_;
struct sigaction old_sigsave_signal_handler_;
bool signal_sender_launched_;
pthread_t signal_sender_thread_;
#endif
};
#endif /* ndef TOOLS_PLATFORM_H_ */

View file

@ -1,145 +0,0 @@
/* 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 "shared-libraries.h"
#define PATH_MAX_TOSTRING(x) #x
#define PATH_MAX_STRING(x) PATH_MAX_TOSTRING(x)
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>
#include <fstream>
#include "platform.h"
#include "shared-libraries.h"
#include "common/linux/file_id.h"
#include <algorithm>
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
// Get the breakpad Id for the binary file pointed by bin_name
static std::string getId(const char *bin_name)
{
using namespace google_breakpad;
using namespace std;
PageAllocator allocator;
auto_wasteful_vector<uint8_t, sizeof(MDGUID)> identifier(&allocator);
FileID file_id(bin_name);
if (file_id.ElfFileIdentifier(identifier)) {
return FileID::ConvertIdentifierToUUIDString(identifier) + "0";
}
return "";
}
// TODO fix me with proper include
#include "nsDebug.h"
#ifdef ANDROID
#include "ElfLoader.h" // dl_phdr_info
#else
#include <link.h> // dl_phdr_info
#endif
#include <features.h>
#include <dlfcn.h>
#include <sys/types.h>
#ifdef ANDROID
extern "C" MOZ_EXPORT __attribute__((weak))
int dl_iterate_phdr(
int (*callback) (struct dl_phdr_info *info,
size_t size, void *data),
void *data);
#endif
static int
dl_iterate_callback(struct dl_phdr_info *dl_info, size_t size, void *data)
{
SharedLibraryInfo& info = *reinterpret_cast<SharedLibraryInfo*>(data);
if (dl_info->dlpi_phnum <= 0)
return 0;
unsigned long libStart = -1;
unsigned long libEnd = 0;
for (size_t i = 0; i < dl_info->dlpi_phnum; i++) {
if (dl_info->dlpi_phdr[i].p_type != PT_LOAD) {
continue;
}
unsigned long start = dl_info->dlpi_addr + dl_info->dlpi_phdr[i].p_vaddr;
unsigned long end = start + dl_info->dlpi_phdr[i].p_memsz;
if (start < libStart)
libStart = start;
if (end > libEnd)
libEnd = end;
}
const char *name = dl_info->dlpi_name;
SharedLibrary shlib(libStart, libEnd, 0, getId(name), name);
info.AddSharedLibrary(shlib);
return 0;
}
SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf()
{
SharedLibraryInfo info;
#ifdef ANDROID
if (!dl_iterate_phdr) {
// On ARM Android, dl_iterate_phdr is provided by the custom linker.
// So if libxul was loaded by the system linker (e.g. as part of
// xpcshell when running tests), it won't be available and we should
// not call it.
return info;
}
#endif // ANDROID
dl_iterate_phdr(dl_iterate_callback, &info);
#if defined(ANDROID)
pid_t pid = getpid();
char path[PATH_MAX];
snprintf(path, PATH_MAX, "/proc/%d/maps", pid);
std::ifstream maps(path);
std::string line;
int count = 0;
while (std::getline(maps, line)) {
int ret;
//XXX: needs input sanitizing
unsigned long start;
unsigned long end;
char perm[6] = "";
unsigned long offset;
char name[PATH_MAX] = "";
ret = sscanf(line.c_str(),
"%lx-%lx %6s %lx %*s %*x %" PATH_MAX_STRING(PATH_MAX) "s\n",
&start, &end, perm, &offset, name);
if (!strchr(perm, 'x')) {
// Ignore non executable entries
continue;
}
if (ret != 5 && ret != 4) {
LOG("Get maps line failed");
continue;
}
// Use proc/pid/maps to get the dalvik-jit section since it has
// no associated phdrs
if (strcmp(name, "/dev/ashmem/dalvik-jit-code-cache") != 0)
continue;
SharedLibrary shlib(start, end, offset, getId(name), name);
info.AddSharedLibrary(shlib);
if (count > 10000) {
LOG("Get maps failed");
break;
}
count++;
}
#endif // ANDROID
return info;
}

View file

@ -1,132 +0,0 @@
/* -*- 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 <AvailabilityMacros.h>
#include <mach-o/loader.h>
#include <mach-o/dyld_images.h>
#include <mach/task_info.h>
#include <mach/task.h>
#include <mach/mach_init.h>
#include <mach/mach_traps.h>
#include <string.h>
#include <stdlib.h>
#include <vector>
#include <sstream>
#include "shared-libraries.h"
#ifndef MAC_OS_X_VERSION_10_6
#define MAC_OS_X_VERSION_10_6 1060
#endif
#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
// borrowed from Breakpad
// Fallback declarations for TASK_DYLD_INFO and friends, introduced in
// <mach/task_info.h> in the Mac OS X 10.6 SDK.
#define TASK_DYLD_INFO 17
struct task_dyld_info {
mach_vm_address_t all_image_info_addr;
mach_vm_size_t all_image_info_size;
};
typedef struct task_dyld_info task_dyld_info_data_t;
typedef struct task_dyld_info *task_dyld_info_t;
#define TASK_DYLD_INFO_COUNT (sizeof(task_dyld_info_data_t) / sizeof(natural_t))
#endif
// Architecture specific abstraction.
#ifdef __i386__
typedef mach_header platform_mach_header;
typedef segment_command mach_segment_command_type;
#define MACHO_MAGIC_NUMBER MH_MAGIC
#define CMD_SEGMENT LC_SEGMENT
#define seg_size uint32_t
#else
typedef mach_header_64 platform_mach_header;
typedef segment_command_64 mach_segment_command_type;
#define MACHO_MAGIC_NUMBER MH_MAGIC_64
#define CMD_SEGMENT LC_SEGMENT_64
#define seg_size uint64_t
#endif
static
void addSharedLibrary(const platform_mach_header* header, char *name, SharedLibraryInfo &info) {
const struct load_command *cmd =
reinterpret_cast<const struct load_command *>(header + 1);
seg_size size = 0;
unsigned long long start = reinterpret_cast<unsigned long long>(header);
// Find the cmd segment in the macho image. It will contain the offset we care about.
const uint8_t *uuid_bytes = nullptr;
for (unsigned int i = 0;
cmd && (i < header->ncmds) && (uuid_bytes == nullptr || size == 0);
++i) {
if (cmd->cmd == CMD_SEGMENT) {
const mach_segment_command_type *seg =
reinterpret_cast<const mach_segment_command_type *>(cmd);
if (!strcmp(seg->segname, "__TEXT")) {
size = seg->vmsize;
}
} else if (cmd->cmd == LC_UUID) {
const uuid_command *ucmd = reinterpret_cast<const uuid_command *>(cmd);
uuid_bytes = ucmd->uuid;
}
cmd = reinterpret_cast<const struct load_command *>
(reinterpret_cast<const char *>(cmd) + cmd->cmdsize);
}
std::stringstream uuid;
uuid << std::hex << std::uppercase;
if (uuid_bytes != nullptr) {
for (int i = 0; i < 16; ++i) {
uuid << ((uuid_bytes[i] & 0xf0) >> 4);
uuid << (uuid_bytes[i] & 0xf);
}
uuid << '0';
}
info.AddSharedLibrary(SharedLibrary(start, start + size, 0, uuid.str(),
name));
}
// Use dyld to inspect the macho image information. We can build the SharedLibraryEntry structure
// giving us roughtly the same info as /proc/PID/maps in Linux.
SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf()
{
SharedLibraryInfo sharedLibraryInfo;
task_dyld_info_data_t task_dyld_info;
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT;
if (task_info(mach_task_self (), TASK_DYLD_INFO, (task_info_t)&task_dyld_info,
&count) != KERN_SUCCESS) {
return sharedLibraryInfo;
}
struct dyld_all_image_infos* aii = (struct dyld_all_image_infos*)task_dyld_info.all_image_info_addr;
size_t infoCount = aii->infoArrayCount;
// Iterate through all dyld images (loaded libraries) to get their names
// and offests.
for (size_t i = 0; i < infoCount; ++i) {
const dyld_image_info *info = &aii->infoArray[i];
// If the magic number doesn't match then go no further
// since we're not pointing to where we think we are.
if (info->imageLoadAddress->magic != MACHO_MAGIC_NUMBER) {
continue;
}
const platform_mach_header* header =
reinterpret_cast<const platform_mach_header*>(info->imageLoadAddress);
// Add the entry for this image.
addSharedLibrary(header, (char*)info->imageFilePath, sharedLibraryInfo);
}
return sharedLibraryInfo;
}

View file

@ -1,137 +0,0 @@
/* -*- 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 <windows.h>
#include <tlhelp32.h>
#include <dbghelp.h>
#include <sstream>
#include "shared-libraries.h"
#include "nsWindowsHelpers.h"
#define CV_SIGNATURE 0x53445352 // 'SDSR'
struct CodeViewRecord70
{
uint32_t signature;
GUID pdbSignature;
uint32_t pdbAge;
char pdbFileName[1];
};
static bool GetPdbInfo(uintptr_t aStart, nsID& aSignature, uint32_t& aAge, char** aPdbName)
{
if (!aStart) {
return false;
}
PIMAGE_DOS_HEADER dosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(aStart);
if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
return false;
}
PIMAGE_NT_HEADERS ntHeaders = reinterpret_cast<PIMAGE_NT_HEADERS>(
aStart + dosHeader->e_lfanew);
if (ntHeaders->Signature != IMAGE_NT_SIGNATURE) {
return false;
}
uint32_t relativeVirtualAddress =
ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress;
if (!relativeVirtualAddress) {
return false;
}
PIMAGE_DEBUG_DIRECTORY debugDirectory =
reinterpret_cast<PIMAGE_DEBUG_DIRECTORY>(aStart + relativeVirtualAddress);
if (!debugDirectory || debugDirectory->Type != IMAGE_DEBUG_TYPE_CODEVIEW) {
return false;
}
CodeViewRecord70 *debugInfo = reinterpret_cast<CodeViewRecord70 *>(
aStart + debugDirectory->AddressOfRawData);
if (!debugInfo || debugInfo->signature != CV_SIGNATURE) {
return false;
}
aAge = debugInfo->pdbAge;
GUID& pdbSignature = debugInfo->pdbSignature;
aSignature.m0 = pdbSignature.Data1;
aSignature.m1 = pdbSignature.Data2;
aSignature.m2 = pdbSignature.Data3;
memcpy(aSignature.m3, pdbSignature.Data4, sizeof(pdbSignature.Data4));
// The PDB file name could be different from module filename, so report both
// e.g. The PDB for C:\Windows\SysWOW64\ntdll.dll is wntdll.pdb
char * leafName = strrchr(debugInfo->pdbFileName, '\\');
if (leafName) {
// Only report the file portion of the path
*aPdbName = leafName + 1;
} else {
*aPdbName = debugInfo->pdbFileName;
}
return true;
}
static bool IsDashOrBraces(char c)
{
return c == '-' || c == '{' || c == '}';
}
SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf()
{
SharedLibraryInfo sharedLibraryInfo;
nsAutoHandle snap(CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId()));
MODULEENTRY32 module = {0};
module.dwSize = sizeof(MODULEENTRY32);
if (Module32First(snap, &module)) {
do {
nsID pdbSig;
uint32_t pdbAge;
char *pdbName = NULL;
// Load the module again to make sure that its handle will remain remain
// valid as we attempt to read the PDB information from it. We load the
// DLL as a datafile so that if the module actually gets unloaded between
// the call to Module32Next and the following LoadLibraryEx, we don't end
// up running the now newly loaded module's DllMain function. If the
// module is already loaded, LoadLibraryEx just increments its refcount.
//
// Note that because of the race condition above, merely loading the DLL
// again is not safe enough, therefore we also need to make sure that we
// can read the memory mapped at the base address before we can safely
// proceed to actually access those pages.
HMODULE handleLock = LoadLibraryEx(module.szExePath, NULL, LOAD_LIBRARY_AS_DATAFILE);
MEMORY_BASIC_INFORMATION vmemInfo = {0};
if (handleLock &&
sizeof(vmemInfo) == VirtualQuery(module.modBaseAddr, &vmemInfo, sizeof(vmemInfo)) &&
vmemInfo.State == MEM_COMMIT &&
GetPdbInfo((uintptr_t)module.modBaseAddr, pdbSig, pdbAge, &pdbName)) {
std::ostringstream stream;
stream << pdbSig.ToString() << std::hex << pdbAge;
std::string breakpadId = stream.str();
std::string::iterator end =
std::remove_if(breakpadId.begin(), breakpadId.end(), IsDashOrBraces);
breakpadId.erase(end, breakpadId.end());
std::transform(breakpadId.begin(), breakpadId.end(),
breakpadId.begin(), toupper);
SharedLibrary shlib((uintptr_t)module.modBaseAddr,
(uintptr_t)module.modBaseAddr+module.modBaseSize,
0, // DLLs are always mapped at offset 0 on Windows
breakpadId,
pdbName);
sharedLibraryInfo.AddSharedLibrary(shlib);
}
FreeLibrary(handleLock); // ok to free null handles
} while (Module32Next(snap, &module));
}
return sharedLibraryInfo;
}

View file

@ -1,207 +0,0 @@
/* 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 "mozilla/ProfileGatherer.h"
#include "mozilla/Services.h"
#include "nsIObserverService.h"
#include "GeckoSampler.h"
using mozilla::dom::AutoJSAPI;
using mozilla::dom::Promise;
namespace mozilla {
/**
* When a subprocess exits before we've gathered profiles, we'll
* store profiles for those processes until gathering starts. We'll
* only store up to MAX_SUBPROCESS_EXIT_PROFILES. The buffer is
* circular, so as soon as we receive another exit profile, we'll
* bump the oldest one out of the buffer.
*/
static const uint32_t MAX_SUBPROCESS_EXIT_PROFILES = 5;
NS_IMPL_ISUPPORTS(ProfileGatherer, nsIObserver)
ProfileGatherer::ProfileGatherer(GeckoSampler* aTicker)
: mTicker(aTicker)
, mSinceTime(0)
, mPendingProfiles(0)
, mGathering(false)
{
}
void
ProfileGatherer::GatheredOOPProfile()
{
MOZ_ASSERT(NS_IsMainThread());
if (!mGathering) {
// If we're not actively gathering, then we don't actually
// care that we gathered a profile here. This can happen for
// processes that exit while profiling.
return;
}
if (NS_WARN_IF(!mPromise)) {
// If we're not holding on to a Promise, then someone is
// calling us erroneously.
return;
}
mPendingProfiles--;
if (mPendingProfiles == 0) {
// We've got all of the async profiles now. Let's
// finish off the profile and resolve the Promise.
Finish();
}
}
void
ProfileGatherer::WillGatherOOPProfile()
{
mPendingProfiles++;
}
void
ProfileGatherer::Start(double aSinceTime,
Promise* aPromise)
{
MOZ_ASSERT(NS_IsMainThread());
if (mGathering) {
// If we're already gathering, reject the promise - this isn't going
// to end well.
if (aPromise) {
aPromise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
}
return;
}
mSinceTime = aSinceTime;
mPromise = aPromise;
mGathering = true;
mPendingProfiles = 0;
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
if (os) {
DebugOnly<nsresult> rv =
os->AddObserver(this, "profiler-subprocess", false);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "AddObserver failed");
rv = os->NotifyObservers(this, "profiler-subprocess-gather", nullptr);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NotifyObservers failed");
}
if (!mPendingProfiles) {
Finish();
}
}
void
ProfileGatherer::Finish()
{
MOZ_ASSERT(NS_IsMainThread());
if (!mTicker) {
// We somehow got called after we were cancelled! This shouldn't
// be possible, but doing a belt-and-suspenders check to be sure.
return;
}
UniquePtr<char[]> buf = mTicker->ToJSON(mSinceTime);
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
if (os) {
DebugOnly<nsresult> rv = os->RemoveObserver(this, "profiler-subprocess");
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "RemoveObserver failed");
}
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(mPromise->GlobalJSObject()))) {
// We're really hosed if we can't get a JS context for some reason.
Reset();
return;
}
JSContext* cx = jsapi.cx();
// Now parse the JSON so that we resolve with a JS Object.
JS::RootedValue val(cx);
{
NS_ConvertUTF8toUTF16 js_string(nsDependentCString(buf.get()));
if (!JS_ParseJSON(cx, static_cast<const char16_t*>(js_string.get()),
js_string.Length(), &val)) {
if (!jsapi.HasException()) {
mPromise->MaybeReject(NS_ERROR_DOM_UNKNOWN_ERR);
} else {
JS::RootedValue exn(cx);
DebugOnly<bool> gotException = jsapi.StealException(&exn);
MOZ_ASSERT(gotException);
jsapi.ClearException();
mPromise->MaybeReject(cx, exn);
}
} else {
mPromise->MaybeResolve(val);
}
}
Reset();
}
void
ProfileGatherer::Reset()
{
mSinceTime = 0;
mPromise = nullptr;
mPendingProfiles = 0;
mGathering = false;
}
void
ProfileGatherer::Cancel()
{
// The GeckoSampler is going away. If we have a Promise in flight, we
// should reject it.
if (mPromise) {
mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
}
// Clear out the GeckoSampler reference, since it's being destroyed.
mTicker = nullptr;
}
void
ProfileGatherer::OOPExitProfile(const nsCString& aProfile)
{
if (mExitProfiles.Length() >= MAX_SUBPROCESS_EXIT_PROFILES) {
mExitProfiles.RemoveElementAt(0);
}
mExitProfiles.AppendElement(aProfile);
// If a process exited while gathering, we need to make
// sure we decrement the counter.
if (mGathering) {
GatheredOOPProfile();
}
}
NS_IMETHODIMP
ProfileGatherer::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t *someData)
{
if (!strcmp(aTopic, "profiler-subprocess")) {
nsCOMPtr<nsIProfileSaveEvent> pse = do_QueryInterface(aSubject);
if (pse) {
for (size_t i = 0; i < mExitProfiles.Length(); ++i) {
if (!mExitProfiles[i].IsEmpty()) {
pse->AddSubProfile(mExitProfiles[i].get());
}
}
mExitProfiles.Clear();
}
}
return NS_OK;
}
} // namespace mozilla

View file

@ -1,16 +0,0 @@
/* 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/. */
"use strict";
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
this.EXPORTED_SYMBOLS = ["Profiler"];
this.Profiler = {
};

View file

@ -1,30 +0,0 @@
/* 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 "GeckoProfiler.h"
#include "ProfilerIOInterposeObserver.h"
#include "ProfilerMarkers.h"
using namespace mozilla;
void ProfilerIOInterposeObserver::Observe(Observation& aObservation)
{
if (!IsMainThread()) {
return;
}
ProfilerBacktrace* stack = profiler_get_backtrace();
nsCString filename;
if (aObservation.Filename()) {
filename = NS_ConvertUTF16toUTF8(aObservation.Filename());
}
IOMarkerPayload* markerPayload = new IOMarkerPayload(aObservation.Reference(),
filename.get(),
aObservation.Start(),
aObservation.End(),
stack);
PROFILER_MARKER_PAYLOAD(aObservation.ObservedOperationString(), markerPayload);
}

View file

@ -5,24 +5,6 @@
#ifndef PROFILERIOINTERPOSEOBSERVER_H
#define PROFILERIOINTERPOSEOBSERVER_H
#ifdef MOZ_ENABLE_PROFILER_SPS
#include "mozilla/IOInterposer.h"
namespace mozilla {
/**
* This class is the observer that calls into the profiler whenever
* main thread I/O occurs.
*/
class ProfilerIOInterposeObserver final : public IOInterposeObserver
{
public:
virtual void Observe(Observation& aObservation);
};
} // namespace mozilla
#endif // MOZ_ENABLE_PROFILER_SPS
/*** STUB ***/
#endif // PROFILERIOINTERPOSEOBSERVER_H

View file

@ -1,45 +0,0 @@
/* -*- 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 "GeckoProfiler.h"
nsresult
SaveProfileTask::Run() {
// Get file path
#if defined(SPS_PLAT_arm_android)
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
profiler_save_profile_to_file(tmpPath.get());
return NS_OK;
}
NS_IMPL_ISUPPORTS(ProfileSaveEvent, nsIProfileSaveEvent)
nsresult
ProfileSaveEvent::AddSubProfile(const char* aProfile) {
mFunc(aProfile, mClosure);
return NS_OK;
}

View file

@ -1,54 +0,0 @@
/* -*- 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/. */
#ifndef PROFILER_SAVETASK_H_
#define PROFILER_SAVETASK_H_
#include "platform.h"
#include "nsThreadUtils.h"
#include "nsIXULRuntime.h"
#include "nsDirectoryServiceUtils.h"
#include "nsDirectoryServiceDefs.h"
#include "nsXULAppAPI.h"
#include "nsIProfileSaveEvent.h"
#ifdef XP_WIN
#include <windows.h>
#define getpid GetCurrentProcessId
#else
#include <unistd.h>
#endif
/**
* This is an event used to save the profile on the main thread
* to be sure that it is not being modified while saving.
*/
class SaveProfileTask : public mozilla::Runnable {
public:
SaveProfileTask() {}
NS_IMETHOD Run();
};
class ProfileSaveEvent final : public nsIProfileSaveEvent {
public:
typedef void (*AddSubProfileFunc)(const char* aProfile, void* aClosure);
NS_DECL_ISUPPORTS
ProfileSaveEvent(AddSubProfileFunc aFunc, void* aClosure)
: mFunc(aFunc)
, mClosure(aClosure)
{}
NS_IMETHOD AddSubProfile(const char* aProfile) override;
private:
~ProfileSaveEvent() {}
AddSubProfileFunc mFunc;
void* mClosure;
};
#endif

View file

@ -1,118 +0,0 @@
/* -*- 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 "ThreadResponsiveness.h"
#include "platform.h"
#include "nsComponentManagerUtils.h"
#include "nsThreadUtils.h"
#include "nsITimer.h"
#include "mozilla/Monitor.h"
#include "ProfileEntry.h"
#include "ThreadProfile.h"
using mozilla::Monitor;
using mozilla::MonitorAutoLock;
using mozilla::TimeStamp;
class CheckResponsivenessTask : public mozilla::Runnable,
public nsITimerCallback {
public:
CheckResponsivenessTask()
: mLastTracerTime(TimeStamp::Now())
, mMonitor("CheckResponsivenessTask")
, mTimer(nullptr)
, mStop(false)
{
MOZ_COUNT_CTOR(CheckResponsivenessTask);
}
protected:
~CheckResponsivenessTask()
{
MOZ_COUNT_DTOR(CheckResponsivenessTask);
}
public:
NS_IMETHOD Run() override
{
MonitorAutoLock mon(mMonitor);
if (mStop)
return NS_OK;
// This is raced on because we might pause the thread here
// for profiling so if we tried to use a monitor to protect
// mLastTracerTime we could deadlock. We're risking seeing
// a partial write which will show up as an outlier in our
// performance data.
mLastTracerTime = TimeStamp::Now();
if (!mTimer) {
mTimer = do_CreateInstance("@mozilla.org/timer;1");
}
mTimer->InitWithCallback(this, 16, nsITimer::TYPE_ONE_SHOT);
return NS_OK;
}
NS_IMETHOD Notify(nsITimer* aTimer) final
{
NS_DispatchToMainThread(this);
return NS_OK;
}
void Terminate() {
MonitorAutoLock mon(mMonitor);
mStop = true;
}
const TimeStamp& GetLastTracerTime() const {
return mLastTracerTime;
}
NS_DECL_ISUPPORTS_INHERITED
private:
TimeStamp mLastTracerTime;
Monitor mMonitor;
nsCOMPtr<nsITimer> mTimer;
bool mStop;
};
NS_IMPL_ISUPPORTS_INHERITED(CheckResponsivenessTask, mozilla::Runnable,
nsITimerCallback)
ThreadResponsiveness::ThreadResponsiveness(ThreadProfile *aThreadProfile)
: mThreadProfile(aThreadProfile)
, mActiveTracerEvent(nullptr)
{
MOZ_COUNT_CTOR(ThreadResponsiveness);
}
ThreadResponsiveness::~ThreadResponsiveness()
{
MOZ_COUNT_DTOR(ThreadResponsiveness);
if (mActiveTracerEvent) {
mActiveTracerEvent->Terminate();
}
}
void
ThreadResponsiveness::Update()
{
if (!mActiveTracerEvent) {
if (mThreadProfile->GetThreadInfo()->IsMainThread()) {
mActiveTracerEvent = new CheckResponsivenessTask();
NS_DispatchToMainThread(mActiveTracerEvent);
} else if (mThreadProfile->GetThreadInfo()->GetThread()) {
mActiveTracerEvent = new CheckResponsivenessTask();
mThreadProfile->GetThreadInfo()->
GetThread()->Dispatch(mActiveTracerEvent, NS_DISPATCH_NORMAL);
}
}
if (mActiveTracerEvent) {
mLastTracerTime = mActiveTracerEvent->GetLastTracerTime();
}
}

View file

@ -1,38 +0,0 @@
/* -*- 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/. */
#ifndef ThreadResponsiveness_h
#define ThreadResponsiveness_h
#include "nsISupports.h"
#include "mozilla/RefPtr.h"
#include "mozilla/TimeStamp.h"
class ThreadProfile;
class CheckResponsivenessTask;
class ThreadResponsiveness {
public:
explicit ThreadResponsiveness(ThreadProfile *aThreadProfile);
~ThreadResponsiveness();
void Update();
mozilla::TimeDuration GetUnresponsiveDuration(const mozilla::TimeStamp& now) const {
return now - mLastTracerTime;
}
bool HasData() const {
return !mLastTracerTime.IsNull();
}
private:
ThreadProfile* mThreadProfile;
RefPtr<CheckResponsivenessTask> mActiveTracerEvent;
mozilla::TimeStamp mLastTracerTime;
};
#endif

View file

@ -1,19 +0,0 @@
/* -*- 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

@ -1,101 +0,0 @@
/* -*- 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"
%{C++
#include "nsTArrayForwardDeclare.h"
class nsCString;
%}
[ref] native StringArrayRef(const nsTArray<nsCString>);
/**
* Start-up parameters for subprocesses are passed through nsIObserverService,
* which, unfortunately, means we need to implement nsISupports in order to
* go through it.
*/
[uuid(0a175ba7-8fcf-4ce9-9c4b-ccc6272f4425)]
interface nsIProfilerStartParams : nsISupports
{
attribute uint32_t entries;
attribute double interval;
[noscript, notxpcom, nostdcall] StringArrayRef getFeatures();
[noscript, notxpcom, nostdcall] StringArrayRef getThreadFilterNames();
};
[scriptable, uuid(ead3f75c-0e0e-4fbb-901c-1e5392ef5b2a)]
interface nsIProfiler : nsISupports
{
boolean CanProfile();
void StartProfiler(in uint32_t aEntries, in double aInterval,
[array, size_is(aFeatureCount)] in string aFeatures,
in uint32_t aFeatureCount,
[array, size_is(aFilterCount), optional] in string aThreadNameFilters,
[optional] in uint32_t aFilterCount);
void StopProfiler();
boolean IsPaused();
void PauseSampling();
void ResumeSampling();
void AddMarker(in string aMarker);
/*
* Returns the JSON string of the profile. If aSinceTime is passed, only
* report samples taken at >= aSinceTime.
*/
string GetProfile([optional] in double aSinceTime);
/*
* Returns a JS object of the profile. If aSinceTime is passed, only report
* samples taken at >= aSinceTime.
*/
[implicit_jscontext]
jsval getProfileData([optional] in double aSinceTime);
[implicit_jscontext]
nsISupports getProfileDataAsync([optional] in double aSinceTime);
boolean IsActive();
void GetFeatures(out uint32_t aCount, [retval, array, size_is(aCount)] out string aFeatures);
/**
* The starting parameters that were sent to the profiler for sampling.
* If the profiler is not currently sampling, this will return null.
*/
readonly attribute nsIProfilerStartParams startParams;
/**
* The profileGatherer will be null if the profiler is not currently
* active.
*/
readonly attribute nsISupports profileGatherer;
void GetBufferInfo(out uint32_t aCurrentPosition, out uint32_t aTotalSize,
out uint32_t aGeneration);
/**
* Returns the elapsed time, in milliseconds, since the profiler's epoch.
* The epoch is guaranteed to be constant for the duration of the
* process, but is otherwise arbitrary.
*/
double getElapsedTime();
/**
* Returns a JSON string of an array of shared library objects.
* Every object has three properties: start, end, and name.
* start and end are integers describing the address range that the library
* occupies in memory. name is the path of the library as a string.
*
* On Windows profiling builds, the shared library objects will have
* additional pdbSignature and pdbAge properties for uniquely identifying
* shared library versions for stack symbolication.
*/
AString getSharedLibraryInformation();
/**
* Dump the collected profile to a file.
*/
void dumpProfileToFile(in string aFilename);
};

View file

@ -1,308 +0,0 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 <string>
#include <sstream>
#include "GeckoProfiler.h"
#include "nsProfiler.h"
#include "nsProfilerStartParams.h"
#include "nsMemory.h"
#include "nsString.h"
#include "mozilla/Services.h"
#include "nsIObserverService.h"
#include "nsIInterfaceRequestor.h"
#include "nsILoadContext.h"
#include "nsIWebNavigation.h"
#include "nsIInterfaceRequestorUtils.h"
#include "shared-libraries.h"
#include "js/Value.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/Promise.h"
using mozilla::ErrorResult;
using mozilla::dom::Promise;
using std::string;
NS_IMPL_ISUPPORTS(nsProfiler, nsIProfiler)
nsProfiler::nsProfiler()
: mLockedForPrivateBrowsing(false)
{
}
nsProfiler::~nsProfiler()
{
nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
if (observerService) {
observerService->RemoveObserver(this, "chrome-document-global-created");
observerService->RemoveObserver(this, "last-pb-context-exited");
}
}
nsresult
nsProfiler::Init() {
nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
if (observerService) {
observerService->AddObserver(this, "chrome-document-global-created", false);
observerService->AddObserver(this, "last-pb-context-exited", false);
}
return NS_OK;
}
NS_IMETHODIMP
nsProfiler::Observe(nsISupports *aSubject,
const char *aTopic,
const char16_t *aData)
{
if (strcmp(aTopic, "chrome-document-global-created") == 0) {
nsCOMPtr<nsIInterfaceRequestor> requestor = do_QueryInterface(aSubject);
nsCOMPtr<nsIWebNavigation> parentWebNav = do_GetInterface(requestor);
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(parentWebNav);
if (loadContext && loadContext->UsePrivateBrowsing() && !mLockedForPrivateBrowsing) {
mLockedForPrivateBrowsing = true;
profiler_lock();
}
} else if (strcmp(aTopic, "last-pb-context-exited") == 0) {
mLockedForPrivateBrowsing = false;
profiler_unlock();
}
return NS_OK;
}
NS_IMETHODIMP
nsProfiler::CanProfile(bool *aCanProfile)
{
*aCanProfile = !mLockedForPrivateBrowsing;
return NS_OK;
}
NS_IMETHODIMP
nsProfiler::StartProfiler(uint32_t aEntries, double aInterval,
const char** aFeatures, uint32_t aFeatureCount,
const char** aThreadNameFilters, uint32_t aFilterCount)
{
if (mLockedForPrivateBrowsing) {
return NS_ERROR_NOT_AVAILABLE;
}
profiler_start(aEntries, aInterval,
aFeatures, aFeatureCount,
aThreadNameFilters, aFilterCount);
return NS_OK;
}
NS_IMETHODIMP
nsProfiler::StopProfiler()
{
profiler_stop();
return NS_OK;
}
NS_IMETHODIMP
nsProfiler::IsPaused(bool *aIsPaused)
{
*aIsPaused = profiler_is_paused();
return NS_OK;
}
NS_IMETHODIMP
nsProfiler::PauseSampling()
{
profiler_pause();
return NS_OK;
}
NS_IMETHODIMP
nsProfiler::ResumeSampling()
{
profiler_resume();
return NS_OK;
}
NS_IMETHODIMP
nsProfiler::AddMarker(const char *aMarker)
{
PROFILER_MARKER(aMarker);
return NS_OK;
}
NS_IMETHODIMP
nsProfiler::GetProfile(double aSinceTime, char** aProfile)
{
mozilla::UniquePtr<char[]> profile = profiler_get_profile(aSinceTime);
if (profile) {
size_t len = strlen(profile.get());
char *profileStr = static_cast<char *>
(nsMemory::Clone(profile.get(), (len + 1) * sizeof(char)));
profileStr[len] = '\0';
*aProfile = profileStr;
}
return NS_OK;
}
std::string GetSharedLibraryInfoStringInternal();
std::string
GetSharedLibraryInfoString()
{
return GetSharedLibraryInfoStringInternal();
}
NS_IMETHODIMP
nsProfiler::GetSharedLibraryInformation(nsAString& aOutString)
{
aOutString.Assign(NS_ConvertUTF8toUTF16(GetSharedLibraryInfoString().c_str()));
return NS_OK;
}
NS_IMETHODIMP
nsProfiler::DumpProfileToFile(const char* aFilename)
{
profiler_save_profile_to_file(aFilename);
return NS_OK;
}
NS_IMETHODIMP
nsProfiler::GetProfileData(double aSinceTime, JSContext* aCx,
JS::MutableHandle<JS::Value> aResult)
{
JS::RootedObject obj(aCx, profiler_get_profile_jsobject(aCx, aSinceTime));
if (!obj) {
return NS_ERROR_FAILURE;
}
aResult.setObject(*obj);
return NS_OK;
}
NS_IMETHODIMP
nsProfiler::GetProfileDataAsync(double aSinceTime, JSContext* aCx,
nsISupports** aPromise)
{
MOZ_ASSERT(NS_IsMainThread());
if (NS_WARN_IF(!aCx)) {
return NS_ERROR_FAILURE;
}
nsIGlobalObject* go = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
if (NS_WARN_IF(!go)) {
return NS_ERROR_FAILURE;
}
ErrorResult result;
RefPtr<Promise> promise = Promise::Create(go, result);
if (NS_WARN_IF(result.Failed())) {
return result.StealNSResult();
}
profiler_get_profile_jsobject_async(aSinceTime, promise);
promise.forget(aPromise);
return NS_OK;
}
NS_IMETHODIMP
nsProfiler::GetElapsedTime(double* aElapsedTime)
{
*aElapsedTime = profiler_time();
return NS_OK;
}
NS_IMETHODIMP
nsProfiler::IsActive(bool *aIsActive)
{
*aIsActive = profiler_is_active();
return NS_OK;
}
NS_IMETHODIMP
nsProfiler::GetFeatures(uint32_t *aCount, char ***aFeatures)
{
uint32_t len = 0;
const char **features = profiler_get_features();
if (!features) {
*aCount = 0;
*aFeatures = nullptr;
return NS_OK;
}
while (features[len]) {
len++;
}
char **featureList = static_cast<char **>
(moz_xmalloc(len * sizeof(char*)));
for (size_t i = 0; i < len; i++) {
size_t strLen = strlen(features[i]);
featureList[i] = static_cast<char *>
(nsMemory::Clone(features[i], (strLen + 1) * sizeof(char)));
}
*aFeatures = featureList;
*aCount = len;
return NS_OK;
}
NS_IMETHODIMP
nsProfiler::GetStartParams(nsIProfilerStartParams** aRetVal)
{
if (!profiler_is_active()) {
*aRetVal = nullptr;
} else {
int entrySize = 0;
double interval = 0;
mozilla::Vector<const char*> filters;
mozilla::Vector<const char*> features;
profiler_get_start_params(&entrySize, &interval, &filters, &features);
nsTArray<nsCString> filtersArray;
for (uint32_t i = 0; i < filters.length(); ++i) {
filtersArray.AppendElement(filters[i]);
}
nsTArray<nsCString> featuresArray;
for (size_t i = 0; i < features.length(); ++i) {
featuresArray.AppendElement(features[i]);
}
nsCOMPtr<nsIProfilerStartParams> startParams =
new nsProfilerStartParams(entrySize, interval, featuresArray,
filtersArray);
startParams.forget(aRetVal);
}
return NS_OK;
}
NS_IMETHODIMP
nsProfiler::GetBufferInfo(uint32_t *aCurrentPosition, uint32_t *aTotalSize, uint32_t *aGeneration)
{
MOZ_ASSERT(aCurrentPosition);
MOZ_ASSERT(aTotalSize);
MOZ_ASSERT(aGeneration);
profiler_get_buffer_info(aCurrentPosition, aTotalSize, aGeneration);
return NS_OK;
}
NS_IMETHODIMP
nsProfiler::GetProfileGatherer(nsISupports** aRetVal)
{
if (!aRetVal) {
return NS_ERROR_INVALID_POINTER;
}
// If we're not profiling, there will be no gatherer.
if (!profiler_is_active()) {
*aRetVal = nullptr;
} else {
nsCOMPtr<nsISupports> gatherer;
profiler_get_gatherer(getter_AddRefs(gatherer));
gatherer.forget(aRetVal);
}
return NS_OK;
}

View file

@ -1,29 +0,0 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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/. */
#ifndef _NSPROFILER_H_
#define _NSPROFILER_H_
#include "nsIProfiler.h"
#include "nsIObserver.h"
#include "mozilla/Attributes.h"
class nsProfiler final : public nsIProfiler, public nsIObserver
{
public:
nsProfiler();
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
NS_DECL_NSIPROFILER
nsresult Init();
private:
~nsProfiler();
bool mLockedForPrivateBrowsing;
};
#endif /* _NSPROFILER_H_ */

View file

@ -1,31 +0,0 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 "mozilla/ModuleUtils.h"
#include "nsCOMPtr.h"
#include "nsProfiler.h"
#include "nsProfilerCIID.h"
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsProfiler, Init)
NS_DEFINE_NAMED_CID(NS_PROFILER_CID);
static const mozilla::Module::CIDEntry kProfilerCIDs[] = {
{ &kNS_PROFILER_CID, false, nullptr, nsProfilerConstructor },
{ nullptr }
};
static const mozilla::Module::ContractIDEntry kProfilerContracts[] = {
{ "@mozilla.org/tools/profiler;1", &kNS_PROFILER_CID },
{ nullptr }
};
static const mozilla::Module kProfilerModule = {
mozilla::Module::kVersion,
kProfilerCIDs,
kProfilerContracts
};
NSMODULE_DEFN(nsProfilerModule) = &kProfilerModule;

View file

@ -1,67 +0,0 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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 "nsProfilerStartParams.h"
NS_IMPL_ISUPPORTS(nsProfilerStartParams, nsIProfilerStartParams)
nsProfilerStartParams::nsProfilerStartParams(uint32_t aEntries,
double aInterval,
const nsTArray<nsCString>& aFeatures,
const nsTArray<nsCString>& aThreadFilterNames) :
mEntries(aEntries),
mInterval(aInterval),
mFeatures(aFeatures),
mThreadFilterNames(aThreadFilterNames)
{
}
nsProfilerStartParams::~nsProfilerStartParams()
{
}
NS_IMETHODIMP
nsProfilerStartParams::GetEntries(uint32_t* aEntries)
{
NS_ENSURE_ARG_POINTER(aEntries);
*aEntries = mEntries;
return NS_OK;
}
NS_IMETHODIMP
nsProfilerStartParams::SetEntries(uint32_t aEntries)
{
NS_ENSURE_ARG(aEntries);
mEntries = aEntries;
return NS_OK;
}
NS_IMETHODIMP
nsProfilerStartParams::GetInterval(double* aInterval)
{
NS_ENSURE_ARG_POINTER(aInterval);
*aInterval = mInterval;
return NS_OK;
}
NS_IMETHODIMP
nsProfilerStartParams::SetInterval(double aInterval)
{
NS_ENSURE_ARG(aInterval);
mInterval = aInterval;
return NS_OK;
}
const nsTArray<nsCString>&
nsProfilerStartParams::GetFeatures()
{
return mFeatures;
}
const nsTArray<nsCString>&
nsProfilerStartParams::GetThreadFilterNames()
{
return mThreadFilterNames;
}

View file

@ -1,32 +0,0 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* 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/. */
#ifndef _NSPROFILERSTARTPARAMS_H_
#define _NSPROFILERSTARTPARAMS_H_
#include "nsIProfiler.h"
#include "nsString.h"
#include "nsTArray.h"
class nsProfilerStartParams : public nsIProfilerStartParams
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIPROFILERSTARTPARAMS
nsProfilerStartParams(uint32_t aEntries,
double aInterval,
const nsTArray<nsCString>& aFeatures,
const nsTArray<nsCString>& aThreadFilterNames);
private:
virtual ~nsProfilerStartParams();
uint32_t mEntries;
double mInterval;
nsTArray<nsCString> mFeatures;
nsTArray<nsCString> mThreadFilterNames;
};
#endif

View file

@ -1,207 +0,0 @@
/* -*- 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 <sys/mman.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "mozilla/Assertions.h"
#include "mozilla/Sprintf.h"
#include "PlatformMacros.h"
#include "AutoObjectMapper.h"
#if defined(SPS_OS_android)
# include <dlfcn.h>
# include "mozilla/Types.h"
// FIXME move these out of mozglue/linker/ElfLoader.h into their
// own header, so as to avoid conflicts arising from two definitions
// of Array
extern "C" {
MFBT_API size_t
__dl_get_mappable_length(void *handle);
MFBT_API void *
__dl_mmap(void *handle, void *addr, size_t length, off_t offset);
MFBT_API void
__dl_munmap(void *handle, void *addr, size_t length);
}
// The following are for get_installation_lib_dir()
# include "nsString.h"
# include "nsDirectoryServiceUtils.h"
# include "nsDirectoryServiceDefs.h"
#endif
// A helper function for creating failure error messages in
// AutoObjectMapper*::Map.
static void
failedToMessage(void(*aLog)(const char*),
const char* aHowFailed, std::string aFileName)
{
char buf[300];
SprintfLiteral(buf, "AutoObjectMapper::Map: Failed to %s \'%s\'",
aHowFailed, aFileName.c_str());
buf[sizeof(buf)-1] = 0;
aLog(buf);
}
AutoObjectMapperPOSIX::AutoObjectMapperPOSIX(void(*aLog)(const char*))
: mImage(nullptr)
, mSize(0)
, mLog(aLog)
, mIsMapped(false)
{}
AutoObjectMapperPOSIX::~AutoObjectMapperPOSIX() {
if (!mIsMapped) {
// There's nothing to do.
MOZ_ASSERT(!mImage);
MOZ_ASSERT(mSize == 0);
return;
}
MOZ_ASSERT(mSize > 0);
// The following assertion doesn't necessarily have to be true,
// but we assume (reasonably enough) that no mmap facility would
// be crazy enough to map anything at page zero.
MOZ_ASSERT(mImage);
munmap(mImage, mSize);
}
bool AutoObjectMapperPOSIX::Map(/*OUT*/void** start, /*OUT*/size_t* length,
std::string fileName)
{
MOZ_ASSERT(!mIsMapped);
int fd = open(fileName.c_str(), O_RDONLY);
if (fd == -1) {
failedToMessage(mLog, "open", fileName);
return false;
}
struct stat st;
int err = fstat(fd, &st);
size_t sz = (err == 0) ? st.st_size : 0;
if (err != 0 || sz == 0) {
failedToMessage(mLog, "fstat", fileName);
close(fd);
return false;
}
void* image = mmap(nullptr, sz, PROT_READ, MAP_SHARED, fd, 0);
if (image == MAP_FAILED) {
failedToMessage(mLog, "mmap", fileName);
close(fd);
return false;
}
close(fd);
mIsMapped = true;
mImage = *start = image;
mSize = *length = sz;
return true;
}
#if defined(SPS_OS_android)
// A helper function for AutoObjectMapperFaultyLib::Map. Finds out
// where the installation's lib directory is, since we'll have to look
// in there to get hold of libmozglue.so. Returned C string is heap
// allocated and the caller must deallocate it.
static char*
get_installation_lib_dir()
{
nsCOMPtr<nsIProperties>
directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
if (!directoryService) {
return nullptr;
}
nsCOMPtr<nsIFile> greDir;
nsresult rv = directoryService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile),
getter_AddRefs(greDir));
if (NS_FAILED(rv)) return nullptr;
nsCString path;
rv = greDir->GetNativePath(path);
if (NS_FAILED(rv)) {
return nullptr;
}
return strdup(path.get());
}
AutoObjectMapperFaultyLib::AutoObjectMapperFaultyLib(void(*aLog)(const char*))
: AutoObjectMapperPOSIX(aLog)
, mHdl(nullptr)
{}
AutoObjectMapperFaultyLib::~AutoObjectMapperFaultyLib() {
if (mHdl) {
// We've got an object mapped by faulty.lib. Unmap it via faulty.lib.
MOZ_ASSERT(mSize > 0);
// Assert on the basis that no valid mapping would start at page zero.
MOZ_ASSERT(mImage);
__dl_munmap(mHdl, mImage, mSize);
dlclose(mHdl);
// Stop assertions in ~AutoObjectMapperPOSIX from failing.
mImage = nullptr;
mSize = 0;
}
// At this point the parent class destructor, ~AutoObjectMapperPOSIX,
// gets called. If that has something mapped in the normal way, it
// will unmap it in the normal way. Unfortunately there's no
// obvious way to enforce the requirement that the object is mapped
// either by faulty.lib or by the parent class, but not by both.
}
bool AutoObjectMapperFaultyLib::Map(/*OUT*/void** start, /*OUT*/size_t* length,
std::string fileName)
{
MOZ_ASSERT(!mHdl);
if (fileName == "libmozglue.so") {
// Do (2) in the comment above.
char* libdir = get_installation_lib_dir();
if (libdir) {
fileName = std::string(libdir) + "/lib/" + fileName;
free(libdir);
}
// Hand the problem off to the standard mapper.
return AutoObjectMapperPOSIX::Map(start, length, fileName);
} else {
// Do cases (1) and (3) in the comment above. We have to
// grapple with faulty.lib directly.
void* hdl = dlopen(fileName.c_str(), RTLD_GLOBAL | RTLD_LAZY);
if (!hdl) {
failedToMessage(mLog, "get handle for ELF file", fileName);
return false;
}
size_t sz = __dl_get_mappable_length(hdl);
if (sz == 0) {
dlclose(hdl);
failedToMessage(mLog, "get size for ELF file", fileName);
return false;
}
void* image = __dl_mmap(hdl, nullptr, sz, 0);
if (image == MAP_FAILED) {
dlclose(hdl);
failedToMessage(mLog, "mmap ELF file", fileName);
return false;
}
mHdl = hdl;
mImage = *start = image;
mSize = *length = sz;
return true;
}
}
#endif // defined(SPS_OS_android)

View file

@ -1,115 +0,0 @@
/* -*- 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/. */
#ifndef AutoObjectMapper_h
#define AutoObjectMapper_h
#include <string>
#include "mozilla/Attributes.h"
#include "PlatformMacros.h"
// A (nearly-) RAII class that maps an object in and then unmaps it on
// destruction. This base class version uses the "normal" POSIX
// functions: open, fstat, close, mmap, munmap.
class MOZ_STACK_CLASS AutoObjectMapperPOSIX {
public:
// The constructor does not attempt to map the file, because that
// might fail. Instead, once the object has been constructed,
// call Map() to attempt the mapping. There is no corresponding
// Unmap() since the unmapping is done in the destructor. Failure
// messages are sent to |aLog|.
explicit AutoObjectMapperPOSIX(void(*aLog)(const char*));
// Unmap the file on destruction of this object.
~AutoObjectMapperPOSIX();
// Map |fileName| into the address space and return the mapping
// extents. If the file is zero sized this will fail. The file is
// mapped read-only and private. Returns true iff the mapping
// succeeded, in which case *start and *length hold its extent.
// Once a call to Map succeeds, all subsequent calls to it will
// fail.
bool Map(/*OUT*/void** start, /*OUT*/size_t* length, std::string fileName);
protected:
// If we are currently holding a mapped object, these record the
// mapped address range.
void* mImage;
size_t mSize;
// A logging sink, for complaining about mapping failures.
void (*mLog)(const char*);
private:
// Are we currently holding a mapped object? This is private to
// the base class. Derived classes need to have their own way to
// track whether they are holding a mapped object.
bool mIsMapped;
// Disable copying and assignment.
AutoObjectMapperPOSIX(const AutoObjectMapperPOSIX&);
AutoObjectMapperPOSIX& operator=(const AutoObjectMapperPOSIX&);
// Disable heap allocation of this class.
void* operator new(size_t);
void* operator new[](size_t);
void operator delete(void*);
void operator delete[](void*);
};
#if defined(SPS_OS_android)
// This is a variant of AutoObjectMapperPOSIX suitable for use in
// conjunction with faulty.lib on Android. How it behaves depends on
// the name of the file to be mapped. There are three possible cases:
//
// (1) /foo/bar/xyzzy/blah.apk!/libwurble.so
// We hand it as-is to faulty.lib and let it fish the relevant
// bits out of the APK.
//
// (2) libmozglue.so
// This is part of the Fennec installation, but is not in the
// APK. Instead we have to figure out the installation path
// and look for it there. Because of faulty.lib limitations,
// we have to use regular open/mmap instead of faulty.lib.
//
// (3) libanythingelse.so
// faulty.lib assumes this is a system library, and prepends
// "/system/lib/" to the path. So as in (1), we can give it
// as-is to faulty.lib.
//
// Hence (1) and (3) require special-casing here. Case (2) simply
// hands the problem to the parent class.
class MOZ_STACK_CLASS AutoObjectMapperFaultyLib : public AutoObjectMapperPOSIX {
public:
AutoObjectMapperFaultyLib(void(*aLog)(const char*));
~AutoObjectMapperFaultyLib();
bool Map(/*OUT*/void** start, /*OUT*/size_t* length, std::string fileName);
private:
// faulty.lib requires us to maintain an abstract handle that can be
// used later to unmap the area. If this is non-NULL, it is assumed
// that unmapping is to be done by faulty.lib. Otherwise it goes
// via the normal mechanism.
void* mHdl;
// Disable copying and assignment.
AutoObjectMapperFaultyLib(const AutoObjectMapperFaultyLib&);
AutoObjectMapperFaultyLib& operator=(const AutoObjectMapperFaultyLib&);
// Disable heap allocation of this class.
void* operator new(size_t);
void* operator new[](size_t);
void operator delete(void*);
void operator delete[](void*);
};
#endif // defined(SPS_OS_android)
#endif // AutoObjectMapper_h

View file

@ -1,114 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2011, 2013 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// This file is derived from the following files in
// toolkit/crashreporter/google-breakpad:
// src/common/module.cc
// src/common/unique_string.cc
// There's no internal-only interface for LulCommon. Hence include
// the external interface directly.
#include "LulCommonExt.h"
#include <stdlib.h>
#include <string.h>
#include <string>
#include <map>
namespace lul {
using std::string;
////////////////////////////////////////////////////////////////
// Module
//
Module::Module(const string &name, const string &os,
const string &architecture, const string &id) :
name_(name),
os_(os),
architecture_(architecture),
id_(id) { }
Module::~Module() {
}
////////////////////////////////////////////////////////////////
// UniqueString
//
class UniqueString {
public:
explicit UniqueString(string str) { str_ = strdup(str.c_str()); }
~UniqueString() { free(reinterpret_cast<void*>(const_cast<char*>(str_))); }
const char* str_;
};
const char* FromUniqueString(const UniqueString* ustr)
{
return ustr->str_;
}
bool IsEmptyUniqueString(const UniqueString* ustr)
{
return (ustr->str_)[0] == '\0';
}
////////////////////////////////////////////////////////////////
// UniqueStringUniverse
//
UniqueStringUniverse::~UniqueStringUniverse()
{
for (std::map<string, UniqueString*>::iterator it = map_.begin();
it != map_.end(); it++) {
delete it->second;
}
}
const UniqueString* UniqueStringUniverse::ToUniqueString(string str)
{
std::map<string, UniqueString*>::iterator it = map_.find(str);
if (it == map_.end()) {
UniqueString* ustr = new UniqueString(str);
map_[str] = ustr;
return ustr;
} else {
return it->second;
}
}
} // namespace lul

File diff suppressed because it is too large Load diff

View file

@ -1,359 +0,0 @@
/* -*- 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 "LulDwarfSummariser.h"
#include "mozilla/Assertions.h"
// Set this to 1 for verbose logging
#define DEBUG_SUMMARISER 0
namespace lul {
// Do |s64|'s lowest 32 bits sign extend back to |s64| itself?
static inline bool fitsIn32Bits(int64 s64) {
return s64 == ((s64 & 0xffffffff) ^ 0x80000000) - 0x80000000;
}
// Check a LExpr prefix expression, starting at pfxInstrs[start] up to
// the next PX_End instruction, to ensure that:
// * It only mentions registers that are tracked on this target
// * The start point is sane
// If the expression is ok, return NULL. Else return a pointer
// a const char* holding a bit of text describing the problem.
static const char*
checkPfxExpr(const vector<PfxInstr>* pfxInstrs, int64_t start)
{
size_t nInstrs = pfxInstrs->size();
if (start < 0 || start >= (ssize_t)nInstrs) {
return "bogus start point";
}
size_t i;
for (i = start; i < nInstrs; i++) {
PfxInstr pxi = (*pfxInstrs)[i];
if (pxi.mOpcode == PX_End)
break;
if (pxi.mOpcode == PX_DwReg &&
!registerIsTracked((DW_REG_NUMBER)pxi.mOperand)) {
return "uses untracked reg";
}
}
return nullptr; // success
}
Summariser::Summariser(SecMap* aSecMap, uintptr_t aTextBias,
void(*aLog)(const char*))
: mSecMap(aSecMap)
, mTextBias(aTextBias)
, mLog(aLog)
{
mCurrAddr = 0;
mMax1Addr = 0; // Gives an empty range.
// Initialise the running RuleSet to "haven't got a clue" status.
new (&mCurrRules) RuleSet();
}
void
Summariser::Entry(uintptr_t aAddress, uintptr_t aLength)
{
aAddress += mTextBias;
if (DEBUG_SUMMARISER) {
char buf[100];
SprintfLiteral(buf,
"LUL Entry(%llx, %llu)\n",
(unsigned long long int)aAddress,
(unsigned long long int)aLength);
mLog(buf);
}
// This throws away any previous summary, that is, assumes
// that the previous summary, if any, has been properly finished
// by a call to End().
mCurrAddr = aAddress;
mMax1Addr = aAddress + aLength;
new (&mCurrRules) RuleSet();
}
void
Summariser::Rule(uintptr_t aAddress, int aNewReg,
LExprHow how, int16_t oldReg, int64_t offset)
{
aAddress += mTextBias;
if (DEBUG_SUMMARISER) {
char buf[100];
if (how == NODEREF || how == DEREF) {
bool deref = how == DEREF;
SprintfLiteral(buf,
"LUL 0x%llx old-r%d = %sr%d + %lld%s\n",
(unsigned long long int)aAddress, aNewReg,
deref ? "*(" : "", (int)oldReg, (long long int)offset,
deref ? ")" : "");
} else if (how == PFXEXPR) {
SprintfLiteral(buf,
"LUL 0x%llx old-r%d = pfx-expr-at %lld\n",
(unsigned long long int)aAddress, aNewReg,
(long long int)offset);
} else {
SprintfLiteral(buf,
"LUL 0x%llx old-r%d = (invalid LExpr!)\n",
(unsigned long long int)aAddress, aNewReg);
}
mLog(buf);
}
if (mCurrAddr < aAddress) {
// Flush the existing summary first.
mCurrRules.mAddr = mCurrAddr;
mCurrRules.mLen = aAddress - mCurrAddr;
mSecMap->AddRuleSet(&mCurrRules);
if (DEBUG_SUMMARISER) {
mLog("LUL "); mCurrRules.Print(mLog);
mLog("\n");
}
mCurrAddr = aAddress;
}
// If for some reason summarisation fails, either or both of these
// become non-null and point at constant text describing the
// problem. Using two rather than just one avoids complications of
// having to concatenate two strings to produce a complete error message.
const char* reason1 = nullptr;
const char* reason2 = nullptr;
// |offset| needs to be a 32 bit value that sign extends to 64 bits
// on a 64 bit target. We will need to incorporate |offset| into
// any LExpr made here. So we may as well check it right now.
if (!fitsIn32Bits(offset)) {
reason1 = "offset not in signed 32-bit range";
goto cant_summarise;
}
// FIXME: factor out common parts of the arch-dependent summarisers.
#if defined(LUL_ARCH_arm)
// ----------------- arm ----------------- //
// Now, can we add the rule to our summary? This depends on whether
// the registers and the overall expression are representable. This
// is the heart of the summarisation process.
switch (aNewReg) {
case DW_REG_CFA:
// This is a rule that defines the CFA. The only forms we
// choose to represent are: r7/11/12/13 + offset. The offset
// must fit into 32 bits since 'uintptr_t' is 32 bit on ARM,
// hence there is no need to check it for overflow.
if (how != NODEREF) {
reason1 = "rule for DW_REG_CFA: invalid |how|";
goto cant_summarise;
}
switch (oldReg) {
case DW_REG_ARM_R7: case DW_REG_ARM_R11:
case DW_REG_ARM_R12: case DW_REG_ARM_R13:
break;
default:
reason1 = "rule for DW_REG_CFA: invalid |oldReg|";
goto cant_summarise;
}
mCurrRules.mCfaExpr = LExpr(how, oldReg, offset);
break;
case DW_REG_ARM_R7: case DW_REG_ARM_R11: case DW_REG_ARM_R12:
case DW_REG_ARM_R13: case DW_REG_ARM_R14: case DW_REG_ARM_R15: {
// This is a new rule for R7, R11, R12, R13 (SP), R14 (LR) or
// R15 (the return address).
switch (how) {
case NODEREF: case DEREF:
// Check the old register is one we're tracking.
if (!registerIsTracked((DW_REG_NUMBER)oldReg) &&
oldReg != DW_REG_CFA) {
reason1 = "rule for R7/11/12/13/14/15: uses untracked reg";
goto cant_summarise;
}
break;
case PFXEXPR: {
// Check that the prefix expression only mentions tracked registers.
const vector<PfxInstr>* pfxInstrs = mSecMap->GetPfxInstrs();
reason2 = checkPfxExpr(pfxInstrs, offset);
if (reason2) {
reason1 = "rule for R7/11/12/13/14/15: ";
goto cant_summarise;
}
break;
}
default:
goto cant_summarise;
}
LExpr expr = LExpr(how, oldReg, offset);
switch (aNewReg) {
case DW_REG_ARM_R7: mCurrRules.mR7expr = expr; break;
case DW_REG_ARM_R11: mCurrRules.mR11expr = expr; break;
case DW_REG_ARM_R12: mCurrRules.mR12expr = expr; break;
case DW_REG_ARM_R13: mCurrRules.mR13expr = expr; break;
case DW_REG_ARM_R14: mCurrRules.mR14expr = expr; break;
case DW_REG_ARM_R15: mCurrRules.mR15expr = expr; break;
default: MOZ_ASSERT(0);
}
break;
}
default:
// Leave |reason1| and |reason2| unset here. This program point
// is reached so often that it causes a flood of "Can't
// summarise" messages. In any case, we don't really care about
// the fact that this summary would produce a new value for a
// register that we're not tracking. We do on the other hand
// care if the summary's expression *uses* a register that we're
// not tracking. But in that case one of the above failures
// should tell us which.
goto cant_summarise;
}
// Mark callee-saved registers (r4 .. r11) as unchanged, if there is
// no other information about them. FIXME: do this just once, at
// the point where the ruleset is committed.
if (mCurrRules.mR7expr.mHow == UNKNOWN) {
mCurrRules.mR7expr = LExpr(NODEREF, DW_REG_ARM_R7, 0);
}
if (mCurrRules.mR11expr.mHow == UNKNOWN) {
mCurrRules.mR11expr = LExpr(NODEREF, DW_REG_ARM_R11, 0);
}
if (mCurrRules.mR12expr.mHow == UNKNOWN) {
mCurrRules.mR12expr = LExpr(NODEREF, DW_REG_ARM_R12, 0);
}
// The old r13 (SP) value before the call is always the same as the
// CFA.
mCurrRules.mR13expr = LExpr(NODEREF, DW_REG_CFA, 0);
// If there's no information about R15 (the return address), say
// it's a copy of R14 (the link register).
if (mCurrRules.mR15expr.mHow == UNKNOWN) {
mCurrRules.mR15expr = LExpr(NODEREF, DW_REG_ARM_R14, 0);
}
#elif defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86)
// ---------------- x64/x86 ---------------- //
// Now, can we add the rule to our summary? This depends on whether
// the registers and the overall expression are representable. This
// is the heart of the summarisation process.
switch (aNewReg) {
case DW_REG_CFA:
// This is a rule that defines the CFA. The only forms we can
// represent are: = SP+offset or = FP+offset.
if (how != NODEREF) {
reason1 = "rule for DW_REG_CFA: invalid |how|";
goto cant_summarise;
}
if (oldReg != DW_REG_INTEL_XSP && oldReg != DW_REG_INTEL_XBP) {
reason1 = "rule for DW_REG_CFA: invalid |oldReg|";
goto cant_summarise;
}
mCurrRules.mCfaExpr = LExpr(how, oldReg, offset);
break;
case DW_REG_INTEL_XSP: case DW_REG_INTEL_XBP: case DW_REG_INTEL_XIP: {
// This is a new rule for XSP, XBP or XIP (the return address).
switch (how) {
case NODEREF: case DEREF:
// Check the old register is one we're tracking.
if (!registerIsTracked((DW_REG_NUMBER)oldReg) &&
oldReg != DW_REG_CFA) {
reason1 = "rule for XSP/XBP/XIP: uses untracked reg";
goto cant_summarise;
}
break;
case PFXEXPR: {
// Check that the prefix expression only mentions tracked registers.
const vector<PfxInstr>* pfxInstrs = mSecMap->GetPfxInstrs();
reason2 = checkPfxExpr(pfxInstrs, offset);
if (reason2) {
reason1 = "rule for XSP/XBP/XIP: ";
goto cant_summarise;
}
break;
}
default:
goto cant_summarise;
}
LExpr expr = LExpr(how, oldReg, offset);
switch (aNewReg) {
case DW_REG_INTEL_XBP: mCurrRules.mXbpExpr = expr; break;
case DW_REG_INTEL_XSP: mCurrRules.mXspExpr = expr; break;
case DW_REG_INTEL_XIP: mCurrRules.mXipExpr = expr; break;
default: MOZ_CRASH("impossible value for aNewReg");
}
break;
}
default:
// Leave |reason1| and |reason2| unset here, for the reasons
// explained in the analogous point in the ARM case just above.
goto cant_summarise;
}
// On Intel, it seems the old SP value before the call is always the
// same as the CFA. Therefore, in the absence of any other way to
// recover the SP, specify that the CFA should be copied.
if (mCurrRules.mXspExpr.mHow == UNKNOWN) {
mCurrRules.mXspExpr = LExpr(NODEREF, DW_REG_CFA, 0);
}
// Also, gcc says "Undef" for BP when it is unchanged.
if (mCurrRules.mXbpExpr.mHow == UNKNOWN) {
mCurrRules.mXbpExpr = LExpr(NODEREF, DW_REG_INTEL_XBP, 0);
}
#else
# error "Unsupported arch"
#endif
return;
cant_summarise:
if (reason1 || reason2) {
char buf[200];
SprintfLiteral(buf, "LUL can't summarise: "
"SVMA=0x%llx: %s%s, expr=LExpr(%s,%u,%lld)\n",
(unsigned long long int)(aAddress - mTextBias),
reason1 ? reason1 : "", reason2 ? reason2 : "",
NameOf_LExprHow(how),
(unsigned int)oldReg, (long long int)offset);
mLog(buf);
}
}
uint32_t
Summariser::AddPfxInstr(PfxInstr pfxi)
{
return mSecMap->AddPfxInstr(pfxi);
}
void
Summariser::End()
{
if (DEBUG_SUMMARISER) {
mLog("LUL End\n");
}
if (mCurrAddr < mMax1Addr) {
mCurrRules.mAddr = mCurrAddr;
mCurrRules.mLen = mMax1Addr - mCurrAddr;
mSecMap->AddRuleSet(&mCurrRules);
if (DEBUG_SUMMARISER) {
mLog("LUL "); mCurrRules.Print(mLog);
mLog("\n");
}
}
}
} // namespace lul

View file

@ -1,65 +0,0 @@
/* -*- 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/. */
#ifndef LulDwarfSummariser_h
#define LulDwarfSummariser_h
#include "LulMainInt.h"
namespace lul {
class Summariser
{
public:
Summariser(SecMap* aSecMap, uintptr_t aTextBias, void(*aLog)(const char*));
virtual void Entry(uintptr_t aAddress, uintptr_t aLength);
virtual void End();
// Tell the summariser that the value for |aNewReg| at |aAddress| is
// recovered using the LExpr that can be constructed using the
// components |how|, |oldReg| and |offset|. The summariser will
// inspect the components and may reject them for various reasons,
// but the hope is that it will find them acceptable and record this
// rule permanently.
virtual void Rule(uintptr_t aAddress, int aNewReg,
LExprHow how, int16_t oldReg, int64_t offset);
virtual uint32_t AddPfxInstr(PfxInstr pfxi);
// Send output to the logging sink, for debugging.
virtual void Log(const char* str) { mLog(str); }
private:
// The SecMap in which we park the finished summaries (RuleSets) and
// also any PfxInstrs derived from Dwarf expressions.
SecMap* mSecMap;
// Running state for the current summary (RuleSet) under construction.
RuleSet mCurrRules;
// The start of the address range to which the RuleSet under
// construction applies.
uintptr_t mCurrAddr;
// The highest address, plus one, for which the RuleSet under
// construction could possibly apply. If there are no further
// incoming events then mCurrRules will eventually be emitted
// as-is, for the range mCurrAddr.. mMax1Addr - 1, if that is
// nonempty.
uintptr_t mMax1Addr;
// The bias value (to add to the SVMAs, to get AVMAs) to be used
// when adding entries into mSecMap.
uintptr_t mTextBias;
// A logging sink, for debugging.
void (*mLog)(const char* aFmt);
};
} // namespace lul
#endif // LulDwarfSummariser_h

View file

@ -1,915 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
// Copyright (c) 2006, 2011, 2012 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Restructured in 2009 by: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// (derived from)
// dump_symbols.cc: implement google_breakpad::WriteSymbolFile:
// Find all the debugging info in a file and dump it as a Breakpad symbol file.
//
// dump_symbols.h: Read debugging information from an ELF file, and write
// it out as a Breakpad symbol file.
// This file is derived from the following files in
// toolkit/crashreporter/google-breakpad:
// src/common/linux/dump_symbols.cc
// src/common/linux/elfutils.cc
// src/common/linux/file_id.cc
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <set>
#include <string>
#include <vector>
#include "mozilla/Assertions.h"
#include "mozilla/Sprintf.h"
#include "LulPlatformMacros.h"
#include "LulCommonExt.h"
#include "LulDwarfExt.h"
#include "LulElfInt.h"
#include "LulMainInt.h"
#if defined(LUL_PLAT_arm_android) && !defined(SHT_ARM_EXIDX)
// bionic and older glibsc don't define it
# define SHT_ARM_EXIDX (SHT_LOPROC + 1)
#endif
// This namespace contains helper functions.
namespace {
using lul::DwarfCFIToModule;
using lul::FindElfSectionByName;
using lul::GetOffset;
using lul::IsValidElf;
using lul::Module;
using lul::UniqueStringUniverse;
using lul::scoped_ptr;
using lul::Summariser;
using std::string;
using std::vector;
using std::set;
//
// FDWrapper
//
// Wrapper class to make sure opened file is closed.
//
class FDWrapper {
public:
explicit FDWrapper(int fd) :
fd_(fd) {}
~FDWrapper() {
if (fd_ != -1)
close(fd_);
}
int get() {
return fd_;
}
int release() {
int fd = fd_;
fd_ = -1;
return fd;
}
private:
int fd_;
};
//
// MmapWrapper
//
// Wrapper class to make sure mapped regions are unmapped.
//
class MmapWrapper {
public:
MmapWrapper() : is_set_(false), base_(NULL), size_(0){}
~MmapWrapper() {
if (is_set_ && base_ != NULL) {
MOZ_ASSERT(size_ > 0);
munmap(base_, size_);
}
}
void set(void *mapped_address, size_t mapped_size) {
is_set_ = true;
base_ = mapped_address;
size_ = mapped_size;
}
void release() {
MOZ_ASSERT(is_set_);
is_set_ = false;
base_ = NULL;
size_ = 0;
}
private:
bool is_set_;
void *base_;
size_t size_;
};
// Set NUM_DW_REGNAMES to be the number of Dwarf register names
// appropriate to the machine architecture given in HEADER. Return
// true on success, or false if HEADER's machine architecture is not
// supported.
template<typename ElfClass>
bool DwarfCFIRegisterNames(const typename ElfClass::Ehdr* elf_header,
unsigned int* num_dw_regnames) {
switch (elf_header->e_machine) {
case EM_386:
*num_dw_regnames = DwarfCFIToModule::RegisterNames::I386();
return true;
case EM_ARM:
*num_dw_regnames = DwarfCFIToModule::RegisterNames::ARM();
return true;
case EM_X86_64:
*num_dw_regnames = DwarfCFIToModule::RegisterNames::X86_64();
return true;
default:
MOZ_ASSERT(0);
return false;
}
}
template<typename ElfClass>
bool LoadDwarfCFI(const string& dwarf_filename,
const typename ElfClass::Ehdr* elf_header,
const char* section_name,
const typename ElfClass::Shdr* section,
const bool eh_frame,
const typename ElfClass::Shdr* got_section,
const typename ElfClass::Shdr* text_section,
const bool big_endian,
SecMap* smap,
uintptr_t text_bias,
UniqueStringUniverse* usu,
void (*log)(const char*)) {
// Find the appropriate set of register names for this file's
// architecture.
unsigned int num_dw_regs = 0;
if (!DwarfCFIRegisterNames<ElfClass>(elf_header, &num_dw_regs)) {
fprintf(stderr, "%s: unrecognized ELF machine architecture '%d';"
" cannot convert DWARF call frame information\n",
dwarf_filename.c_str(), elf_header->e_machine);
return false;
}
const lul::Endianness endianness
= big_endian ? lul::ENDIANNESS_BIG : lul::ENDIANNESS_LITTLE;
// Find the call frame information and its size.
const char* cfi =
GetOffset<ElfClass, char>(elf_header, section->sh_offset);
size_t cfi_size = section->sh_size;
// Plug together the parser, handler, and their entourages.
// Here's a summariser, which will receive the output of the
// parser, create summaries, and add them to |smap|.
Summariser summ(smap, text_bias, log);
lul::ByteReader reader(endianness);
reader.SetAddressSize(ElfClass::kAddrSize);
DwarfCFIToModule::Reporter module_reporter(log, dwarf_filename, section_name);
DwarfCFIToModule handler(num_dw_regs, &module_reporter, &reader, usu, &summ);
// Provide the base addresses for .eh_frame encoded pointers, if
// possible.
reader.SetCFIDataBase(section->sh_addr, cfi);
if (got_section)
reader.SetDataBase(got_section->sh_addr);
if (text_section)
reader.SetTextBase(text_section->sh_addr);
lul::CallFrameInfo::Reporter dwarf_reporter(log, dwarf_filename,
section_name);
lul::CallFrameInfo parser(cfi, cfi_size,
&reader, &handler, &dwarf_reporter,
eh_frame);
parser.Start();
return true;
}
bool LoadELF(const string& obj_file, MmapWrapper* map_wrapper,
void** elf_header) {
int obj_fd = open(obj_file.c_str(), O_RDONLY);
if (obj_fd < 0) {
fprintf(stderr, "Failed to open ELF file '%s': %s\n",
obj_file.c_str(), strerror(errno));
return false;
}
FDWrapper obj_fd_wrapper(obj_fd);
struct stat st;
if (fstat(obj_fd, &st) != 0 && st.st_size <= 0) {
fprintf(stderr, "Unable to fstat ELF file '%s': %s\n",
obj_file.c_str(), strerror(errno));
return false;
}
// Mapping it read-only is good enough. In any case, mapping it
// read-write confuses Valgrind's debuginfo acquire/discard
// heuristics, making it hard to profile the profiler.
void *obj_base = mmap(nullptr, st.st_size,
PROT_READ, MAP_PRIVATE, obj_fd, 0);
if (obj_base == MAP_FAILED) {
fprintf(stderr, "Failed to mmap ELF file '%s': %s\n",
obj_file.c_str(), strerror(errno));
return false;
}
map_wrapper->set(obj_base, st.st_size);
*elf_header = obj_base;
if (!IsValidElf(*elf_header)) {
fprintf(stderr, "Not a valid ELF file: %s\n", obj_file.c_str());
return false;
}
return true;
}
// Get the endianness of ELF_HEADER. If it's invalid, return false.
template<typename ElfClass>
bool ElfEndianness(const typename ElfClass::Ehdr* elf_header,
bool* big_endian) {
if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB) {
*big_endian = false;
return true;
}
if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB) {
*big_endian = true;
return true;
}
fprintf(stderr, "bad data encoding in ELF header: %d\n",
elf_header->e_ident[EI_DATA]);
return false;
}
//
// LoadSymbolsInfo
//
// Holds the state between the two calls to LoadSymbols() in case it's necessary
// to follow the .gnu_debuglink section and load debug information from a
// different file.
//
template<typename ElfClass>
class LoadSymbolsInfo {
public:
typedef typename ElfClass::Addr Addr;
explicit LoadSymbolsInfo(const vector<string>& dbg_dirs) :
debug_dirs_(dbg_dirs),
has_loading_addr_(false) {}
// Keeps track of which sections have been loaded so sections don't
// accidentally get loaded twice from two different files.
void LoadedSection(const string &section) {
if (loaded_sections_.count(section) == 0) {
loaded_sections_.insert(section);
} else {
fprintf(stderr, "Section %s has already been loaded.\n",
section.c_str());
}
}
string debuglink_file() const {
return debuglink_file_;
}
private:
const vector<string>& debug_dirs_; // Directories in which to
// search for the debug ELF file.
string debuglink_file_; // Full path to the debug ELF file.
bool has_loading_addr_; // Indicate if LOADING_ADDR_ is valid.
set<string> loaded_sections_; // Tracks the Loaded ELF sections
// between calls to LoadSymbols().
};
// Find the preferred loading address of the binary.
template<typename ElfClass>
typename ElfClass::Addr GetLoadingAddress(
const typename ElfClass::Phdr* program_headers,
int nheader) {
typedef typename ElfClass::Phdr Phdr;
// For non-PIC executables (e_type == ET_EXEC), the load address is
// the start address of the first PT_LOAD segment. (ELF requires
// the segments to be sorted by load address.) For PIC executables
// and dynamic libraries (e_type == ET_DYN), this address will
// normally be zero.
for (int i = 0; i < nheader; ++i) {
const Phdr& header = program_headers[i];
if (header.p_type == PT_LOAD)
return header.p_vaddr;
}
return 0;
}
template<typename ElfClass>
bool LoadSymbols(const string& obj_file,
const bool big_endian,
const typename ElfClass::Ehdr* elf_header,
const bool read_gnu_debug_link,
LoadSymbolsInfo<ElfClass>* info,
SecMap* smap,
void* rx_avma, size_t rx_size,
UniqueStringUniverse* usu,
void (*log)(const char*)) {
typedef typename ElfClass::Phdr Phdr;
typedef typename ElfClass::Shdr Shdr;
char buf[500];
SprintfLiteral(buf, "LoadSymbols: BEGIN %s\n", obj_file.c_str());
buf[sizeof(buf)-1] = 0;
log(buf);
// This is how the text bias is calculated.
// BEGIN CALCULATE BIAS
uintptr_t loading_addr = GetLoadingAddress<ElfClass>(
GetOffset<ElfClass, Phdr>(elf_header, elf_header->e_phoff),
elf_header->e_phnum);
uintptr_t text_bias = ((uintptr_t)rx_avma) - loading_addr;
SprintfLiteral(buf,
"LoadSymbols: rx_avma=%llx, text_bias=%llx",
(unsigned long long int)(uintptr_t)rx_avma,
(unsigned long long int)text_bias);
buf[sizeof(buf)-1] = 0;
log(buf);
// END CALCULATE BIAS
const Shdr* sections =
GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff);
const Shdr* section_names = sections + elf_header->e_shstrndx;
const char* names =
GetOffset<ElfClass, char>(elf_header, section_names->sh_offset);
const char *names_end = names + section_names->sh_size;
bool found_usable_info = false;
// Dwarf Call Frame Information (CFI) is actually independent from
// the other DWARF debugging information, and can be used alone.
const Shdr* dwarf_cfi_section =
FindElfSectionByName<ElfClass>(".debug_frame", SHT_PROGBITS,
sections, names, names_end,
elf_header->e_shnum);
if (dwarf_cfi_section) {
// Ignore the return value of this function; even without call frame
// information, the other debugging information could be perfectly
// useful.
info->LoadedSection(".debug_frame");
bool result =
LoadDwarfCFI<ElfClass>(obj_file, elf_header, ".debug_frame",
dwarf_cfi_section, false, 0, 0, big_endian,
smap, text_bias, usu, log);
found_usable_info = found_usable_info || result;
if (result)
log("LoadSymbols: read CFI from .debug_frame");
}
// Linux C++ exception handling information can also provide
// unwinding data.
const Shdr* eh_frame_section =
FindElfSectionByName<ElfClass>(".eh_frame", SHT_PROGBITS,
sections, names, names_end,
elf_header->e_shnum);
if (eh_frame_section) {
// Pointers in .eh_frame data may be relative to the base addresses of
// certain sections. Provide those sections if present.
const Shdr* got_section =
FindElfSectionByName<ElfClass>(".got", SHT_PROGBITS,
sections, names, names_end,
elf_header->e_shnum);
const Shdr* text_section =
FindElfSectionByName<ElfClass>(".text", SHT_PROGBITS,
sections, names, names_end,
elf_header->e_shnum);
info->LoadedSection(".eh_frame");
// As above, ignore the return value of this function.
bool result =
LoadDwarfCFI<ElfClass>(obj_file, elf_header, ".eh_frame",
eh_frame_section, true,
got_section, text_section, big_endian,
smap, text_bias, usu, log);
found_usable_info = found_usable_info || result;
if (result)
log("LoadSymbols: read CFI from .eh_frame");
}
SprintfLiteral(buf, "LoadSymbols: END %s\n", obj_file.c_str());
buf[sizeof(buf)-1] = 0;
log(buf);
return found_usable_info;
}
// Return the breakpad symbol file identifier for the architecture of
// ELF_HEADER.
template<typename ElfClass>
const char* ElfArchitecture(const typename ElfClass::Ehdr* elf_header) {
typedef typename ElfClass::Half Half;
Half arch = elf_header->e_machine;
switch (arch) {
case EM_386: return "x86";
case EM_ARM: return "arm";
case EM_MIPS: return "mips";
case EM_PPC64: return "ppc64";
case EM_PPC: return "ppc";
case EM_S390: return "s390";
case EM_SPARC: return "sparc";
case EM_SPARCV9: return "sparcv9";
case EM_X86_64: return "x86_64";
default: return NULL;
}
}
// Format the Elf file identifier in IDENTIFIER as a UUID with the
// dashes removed.
string FormatIdentifier(unsigned char identifier[16]) {
char identifier_str[40];
lul::FileID::ConvertIdentifierToString(
identifier,
identifier_str,
sizeof(identifier_str));
string id_no_dash;
for (int i = 0; identifier_str[i] != '\0'; ++i)
if (identifier_str[i] != '-')
id_no_dash += identifier_str[i];
// Add an extra "0" by the end. PDB files on Windows have an 'age'
// number appended to the end of the file identifier; this isn't
// really used or necessary on other platforms, but be consistent.
id_no_dash += '0';
return id_no_dash;
}
// Return the non-directory portion of FILENAME: the portion after the
// last slash, or the whole filename if there are no slashes.
string BaseFileName(const string &filename) {
// Lots of copies! basename's behavior is less than ideal.
char *c_filename = strdup(filename.c_str());
string base = basename(c_filename);
free(c_filename);
return base;
}
template<typename ElfClass>
bool ReadSymbolDataElfClass(const typename ElfClass::Ehdr* elf_header,
const string& obj_filename,
const vector<string>& debug_dirs,
SecMap* smap, void* rx_avma, size_t rx_size,
UniqueStringUniverse* usu,
void (*log)(const char*)) {
typedef typename ElfClass::Ehdr Ehdr;
unsigned char identifier[16];
if (!lul
::FileID::ElfFileIdentifierFromMappedFile(elf_header, identifier)) {
fprintf(stderr, "%s: unable to generate file identifier\n",
obj_filename.c_str());
return false;
}
const char *architecture = ElfArchitecture<ElfClass>(elf_header);
if (!architecture) {
fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n",
obj_filename.c_str(), elf_header->e_machine);
return false;
}
// Figure out what endianness this file is.
bool big_endian;
if (!ElfEndianness<ElfClass>(elf_header, &big_endian))
return false;
string name = BaseFileName(obj_filename);
string os = "Linux";
string id = FormatIdentifier(identifier);
LoadSymbolsInfo<ElfClass> info(debug_dirs);
if (!LoadSymbols<ElfClass>(obj_filename, big_endian, elf_header,
!debug_dirs.empty(), &info,
smap, rx_avma, rx_size, usu, log)) {
const string debuglink_file = info.debuglink_file();
if (debuglink_file.empty())
return false;
// Load debuglink ELF file.
fprintf(stderr, "Found debugging info in %s\n", debuglink_file.c_str());
MmapWrapper debug_map_wrapper;
Ehdr* debug_elf_header = NULL;
if (!LoadELF(debuglink_file, &debug_map_wrapper,
reinterpret_cast<void**>(&debug_elf_header)))
return false;
// Sanity checks to make sure everything matches up.
const char *debug_architecture =
ElfArchitecture<ElfClass>(debug_elf_header);
if (!debug_architecture) {
fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n",
debuglink_file.c_str(), debug_elf_header->e_machine);
return false;
}
if (strcmp(architecture, debug_architecture)) {
fprintf(stderr, "%s with ELF machine architecture %s does not match "
"%s with ELF architecture %s\n",
debuglink_file.c_str(), debug_architecture,
obj_filename.c_str(), architecture);
return false;
}
bool debug_big_endian;
if (!ElfEndianness<ElfClass>(debug_elf_header, &debug_big_endian))
return false;
if (debug_big_endian != big_endian) {
fprintf(stderr, "%s and %s does not match in endianness\n",
obj_filename.c_str(), debuglink_file.c_str());
return false;
}
if (!LoadSymbols<ElfClass>(debuglink_file, debug_big_endian,
debug_elf_header, false, &info,
smap, rx_avma, rx_size, usu, log)) {
return false;
}
}
return true;
}
} // namespace (anon)
namespace lul {
bool ReadSymbolDataInternal(const uint8_t* obj_file,
const string& obj_filename,
const vector<string>& debug_dirs,
SecMap* smap, void* rx_avma, size_t rx_size,
UniqueStringUniverse* usu,
void (*log)(const char*)) {
if (!IsValidElf(obj_file)) {
fprintf(stderr, "Not a valid ELF file: %s\n", obj_filename.c_str());
return false;
}
int elfclass = ElfClass(obj_file);
if (elfclass == ELFCLASS32) {
return ReadSymbolDataElfClass<ElfClass32>(
reinterpret_cast<const Elf32_Ehdr*>(obj_file),
obj_filename, debug_dirs, smap, rx_avma, rx_size, usu, log);
}
if (elfclass == ELFCLASS64) {
return ReadSymbolDataElfClass<ElfClass64>(
reinterpret_cast<const Elf64_Ehdr*>(obj_file),
obj_filename, debug_dirs, smap, rx_avma, rx_size, usu, log);
}
return false;
}
bool ReadSymbolData(const string& obj_file,
const vector<string>& debug_dirs,
SecMap* smap, void* rx_avma, size_t rx_size,
UniqueStringUniverse* usu,
void (*log)(const char*)) {
MmapWrapper map_wrapper;
void* elf_header = NULL;
if (!LoadELF(obj_file, &map_wrapper, &elf_header))
return false;
return ReadSymbolDataInternal(reinterpret_cast<uint8_t*>(elf_header),
obj_file, debug_dirs,
smap, rx_avma, rx_size, usu, log);
}
namespace {
template<typename ElfClass>
void FindElfClassSection(const char *elf_base,
const char *section_name,
typename ElfClass::Word section_type,
const void **section_start,
int *section_size) {
typedef typename ElfClass::Ehdr Ehdr;
typedef typename ElfClass::Shdr Shdr;
MOZ_ASSERT(elf_base);
MOZ_ASSERT(section_start);
MOZ_ASSERT(section_size);
MOZ_ASSERT(strncmp(elf_base, ELFMAG, SELFMAG) == 0);
const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base);
MOZ_ASSERT(elf_header->e_ident[EI_CLASS] == ElfClass::kClass);
const Shdr* sections =
GetOffset<ElfClass,Shdr>(elf_header, elf_header->e_shoff);
const Shdr* section_names = sections + elf_header->e_shstrndx;
const char* names =
GetOffset<ElfClass,char>(elf_header, section_names->sh_offset);
const char *names_end = names + section_names->sh_size;
const Shdr* section =
FindElfSectionByName<ElfClass>(section_name, section_type,
sections, names, names_end,
elf_header->e_shnum);
if (section != NULL && section->sh_size > 0) {
*section_start = elf_base + section->sh_offset;
*section_size = section->sh_size;
}
}
template<typename ElfClass>
void FindElfClassSegment(const char *elf_base,
typename ElfClass::Word segment_type,
const void **segment_start,
int *segment_size) {
typedef typename ElfClass::Ehdr Ehdr;
typedef typename ElfClass::Phdr Phdr;
MOZ_ASSERT(elf_base);
MOZ_ASSERT(segment_start);
MOZ_ASSERT(segment_size);
MOZ_ASSERT(strncmp(elf_base, ELFMAG, SELFMAG) == 0);
const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base);
MOZ_ASSERT(elf_header->e_ident[EI_CLASS] == ElfClass::kClass);
const Phdr* phdrs =
GetOffset<ElfClass,Phdr>(elf_header, elf_header->e_phoff);
for (int i = 0; i < elf_header->e_phnum; ++i) {
if (phdrs[i].p_type == segment_type) {
*segment_start = elf_base + phdrs[i].p_offset;
*segment_size = phdrs[i].p_filesz;
return;
}
}
}
} // namespace (anon)
bool IsValidElf(const void* elf_base) {
return strncmp(reinterpret_cast<const char*>(elf_base),
ELFMAG, SELFMAG) == 0;
}
int ElfClass(const void* elf_base) {
const ElfW(Ehdr)* elf_header =
reinterpret_cast<const ElfW(Ehdr)*>(elf_base);
return elf_header->e_ident[EI_CLASS];
}
bool FindElfSection(const void *elf_mapped_base,
const char *section_name,
uint32_t section_type,
const void **section_start,
int *section_size,
int *elfclass) {
MOZ_ASSERT(elf_mapped_base);
MOZ_ASSERT(section_start);
MOZ_ASSERT(section_size);
*section_start = NULL;
*section_size = 0;
if (!IsValidElf(elf_mapped_base))
return false;
int cls = ElfClass(elf_mapped_base);
if (elfclass) {
*elfclass = cls;
}
const char* elf_base =
static_cast<const char*>(elf_mapped_base);
if (cls == ELFCLASS32) {
FindElfClassSection<ElfClass32>(elf_base, section_name, section_type,
section_start, section_size);
return *section_start != NULL;
} else if (cls == ELFCLASS64) {
FindElfClassSection<ElfClass64>(elf_base, section_name, section_type,
section_start, section_size);
return *section_start != NULL;
}
return false;
}
bool FindElfSegment(const void *elf_mapped_base,
uint32_t segment_type,
const void **segment_start,
int *segment_size,
int *elfclass) {
MOZ_ASSERT(elf_mapped_base);
MOZ_ASSERT(segment_start);
MOZ_ASSERT(segment_size);
*segment_start = NULL;
*segment_size = 0;
if (!IsValidElf(elf_mapped_base))
return false;
int cls = ElfClass(elf_mapped_base);
if (elfclass) {
*elfclass = cls;
}
const char* elf_base =
static_cast<const char*>(elf_mapped_base);
if (cls == ELFCLASS32) {
FindElfClassSegment<ElfClass32>(elf_base, segment_type,
segment_start, segment_size);
return *segment_start != NULL;
} else if (cls == ELFCLASS64) {
FindElfClassSegment<ElfClass64>(elf_base, segment_type,
segment_start, segment_size);
return *segment_start != NULL;
}
return false;
}
// (derived from)
// file_id.cc: Return a unique identifier for a file
//
// See file_id.h for documentation
//
// ELF note name and desc are 32-bits word padded.
#define NOTE_PADDING(a) ((a + 3) & ~3)
// These functions are also used inside the crashed process, so be safe
// and use the syscall/libc wrappers instead of direct syscalls or libc.
template<typename ElfClass>
static bool ElfClassBuildIDNoteIdentifier(const void *section, int length,
uint8_t identifier[kMDGUIDSize]) {
typedef typename ElfClass::Nhdr Nhdr;
const void* section_end = reinterpret_cast<const char*>(section) + length;
const Nhdr* note_header = reinterpret_cast<const Nhdr*>(section);
while (reinterpret_cast<const void *>(note_header) < section_end) {
if (note_header->n_type == NT_GNU_BUILD_ID)
break;
note_header = reinterpret_cast<const Nhdr*>(
reinterpret_cast<const char*>(note_header) + sizeof(Nhdr) +
NOTE_PADDING(note_header->n_namesz) +
NOTE_PADDING(note_header->n_descsz));
}
if (reinterpret_cast<const void *>(note_header) >= section_end ||
note_header->n_descsz == 0) {
return false;
}
const char* build_id = reinterpret_cast<const char*>(note_header) +
sizeof(Nhdr) + NOTE_PADDING(note_header->n_namesz);
// Copy as many bits of the build ID as will fit
// into the GUID space.
memset(identifier, 0, kMDGUIDSize);
memcpy(identifier, build_id,
std::min(kMDGUIDSize, (size_t)note_header->n_descsz));
return true;
}
// Attempt to locate a .note.gnu.build-id section in an ELF binary
// and copy as many bytes of it as will fit into |identifier|.
static bool FindElfBuildIDNote(const void *elf_mapped_base,
uint8_t identifier[kMDGUIDSize]) {
void* note_section;
int note_size, elfclass;
if ((!FindElfSegment(elf_mapped_base, PT_NOTE,
(const void**)&note_section, &note_size, &elfclass) ||
note_size == 0) &&
(!FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE,
(const void**)&note_section, &note_size, &elfclass) ||
note_size == 0)) {
return false;
}
if (elfclass == ELFCLASS32) {
return ElfClassBuildIDNoteIdentifier<ElfClass32>(note_section, note_size,
identifier);
} else if (elfclass == ELFCLASS64) {
return ElfClassBuildIDNoteIdentifier<ElfClass64>(note_section, note_size,
identifier);
}
return false;
}
// Attempt to locate the .text section of an ELF binary and generate
// a simple hash by XORing the first page worth of bytes into |identifier|.
static bool HashElfTextSection(const void *elf_mapped_base,
uint8_t identifier[kMDGUIDSize]) {
void* text_section;
int text_size;
if (!FindElfSection(elf_mapped_base, ".text", SHT_PROGBITS,
(const void**)&text_section, &text_size, NULL) ||
text_size == 0) {
return false;
}
memset(identifier, 0, kMDGUIDSize);
const uint8_t* ptr = reinterpret_cast<const uint8_t*>(text_section);
const uint8_t* ptr_end = ptr + std::min(text_size, 4096);
while (ptr < ptr_end) {
for (unsigned i = 0; i < kMDGUIDSize; i++)
identifier[i] ^= ptr[i];
ptr += kMDGUIDSize;
}
return true;
}
// static
bool FileID::ElfFileIdentifierFromMappedFile(const void* base,
uint8_t identifier[kMDGUIDSize]) {
// Look for a build id note first.
if (FindElfBuildIDNote(base, identifier))
return true;
// Fall back on hashing the first page of the text section.
return HashElfTextSection(base, identifier);
}
// static
void FileID::ConvertIdentifierToString(const uint8_t identifier[kMDGUIDSize],
char* buffer, int buffer_length) {
uint8_t identifier_swapped[kMDGUIDSize];
// Endian-ness swap to match dump processor expectation.
memcpy(identifier_swapped, identifier, kMDGUIDSize);
uint32_t* data1 = reinterpret_cast<uint32_t*>(identifier_swapped);
*data1 = htonl(*data1);
uint16_t* data2 = reinterpret_cast<uint16_t*>(identifier_swapped + 4);
*data2 = htons(*data2);
uint16_t* data3 = reinterpret_cast<uint16_t*>(identifier_swapped + 6);
*data3 = htons(*data3);
int buffer_idx = 0;
for (unsigned int idx = 0;
(buffer_idx < buffer_length) && (idx < kMDGUIDSize);
++idx) {
int hi = (identifier_swapped[idx] >> 4) & 0x0F;
int lo = (identifier_swapped[idx]) & 0x0F;
if (idx == 4 || idx == 6 || idx == 8 || idx == 10)
buffer[buffer_idx++] = '-';
buffer[buffer_idx++] = (hi >= 10) ? 'A' + hi - 10 : '0' + hi;
buffer[buffer_idx++] = (lo >= 10) ? 'A' + lo - 10 : '0' + lo;
}
// NULL terminate
buffer[(buffer_idx < buffer_length) ? buffer_idx : buffer_idx - 1] = 0;
}
} // namespace lul

File diff suppressed because it is too large Load diff

View file

@ -1,397 +0,0 @@
/* -*- 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/. */
#ifndef LulMain_h
#define LulMain_h
#include "LulPlatformMacros.h"
#include "mozilla/Atomics.h"
// LUL: A Lightweight Unwind Library.
// This file provides the end-user (external) interface for LUL.
// Some comments about naming in the implementation. These are safe
// to ignore if you are merely using LUL, but are important if you
// hack on its internals.
//
// Debuginfo readers in general have tended to use the word "address"
// to mean several different things. This sometimes makes them
// difficult to understand and maintain. LUL tries hard to avoid
// using the word "address" and instead uses the following more
// precise terms:
//
// * SVMA ("Stated Virtual Memory Address"): this is an address of a
// symbol (etc) as it is stated in the symbol table, or other
// metadata, of an object. Such values are typically small and
// start from zero or thereabouts, unless the object has been
// prelinked.
//
// * AVMA ("Actual Virtual Memory Address"): this is the address of a
// symbol (etc) in a running process, that is, once the associated
// object has been mapped into a process. Such values are typically
// much larger than SVMAs, since objects can get mapped arbitrarily
// far along the address space.
//
// * "Bias": the difference between AVMA and SVMA for a given symbol
// (specifically, AVMA - SVMA). The bias is always an integral
// number of pages. Once we know the bias for a given object's
// text section (for example), we can compute the AVMAs of all of
// its text symbols by adding the bias to their SVMAs.
//
// * "Image address": typically, to read debuginfo from an object we
// will temporarily mmap in the file so as to read symbol tables
// etc. Addresses in this temporary mapping are called "Image
// addresses". Note that the temporary mapping is entirely
// unrelated to the mappings of the file that the dynamic linker
// must perform merely in order to get the program to run. Hence
// image addresses are unrelated to either SVMAs or AVMAs.
namespace lul {
// A machine word plus validity tag.
class TaggedUWord {
public:
// RUNS IN NO-MALLOC CONTEXT
// Construct a valid one.
explicit TaggedUWord(uintptr_t w)
: mValue(w)
, mValid(true)
{}
// RUNS IN NO-MALLOC CONTEXT
// Construct an invalid one.
TaggedUWord()
: mValue(0)
, mValid(false)
{}
// RUNS IN NO-MALLOC CONTEXT
TaggedUWord operator+(TaggedUWord rhs) const {
return (Valid() && rhs.Valid()) ? TaggedUWord(Value() + rhs.Value())
: TaggedUWord();
}
// RUNS IN NO-MALLOC CONTEXT
TaggedUWord operator-(TaggedUWord rhs) const {
return (Valid() && rhs.Valid()) ? TaggedUWord(Value() - rhs.Value())
: TaggedUWord();
}
// RUNS IN NO-MALLOC CONTEXT
TaggedUWord operator&(TaggedUWord rhs) const {
return (Valid() && rhs.Valid()) ? TaggedUWord(Value() & rhs.Value())
: TaggedUWord();
}
// RUNS IN NO-MALLOC CONTEXT
TaggedUWord operator|(TaggedUWord rhs) const {
return (Valid() && rhs.Valid()) ? TaggedUWord(Value() | rhs.Value())
: TaggedUWord();
}
// RUNS IN NO-MALLOC CONTEXT
TaggedUWord CmpGEs(TaggedUWord rhs) const {
if (Valid() && rhs.Valid()) {
intptr_t s1 = (intptr_t)Value();
intptr_t s2 = (intptr_t)rhs.Value();
return TaggedUWord(s1 >= s2 ? 1 : 0);
}
return TaggedUWord();
}
// RUNS IN NO-MALLOC CONTEXT
TaggedUWord operator<<(TaggedUWord rhs) const {
if (Valid() && rhs.Valid()) {
uintptr_t shift = rhs.Value();
if (shift < 8 * sizeof(uintptr_t))
return TaggedUWord(Value() << shift);
}
return TaggedUWord();
}
// RUNS IN NO-MALLOC CONTEXT
// Is equal? Note: non-validity on either side gives non-equality.
bool operator==(TaggedUWord other) const {
return (mValid && other.Valid()) ? (mValue == other.Value()) : false;
}
// RUNS IN NO-MALLOC CONTEXT
// Is it word-aligned?
bool IsAligned() const {
return mValid && (mValue & (sizeof(uintptr_t)-1)) == 0;
}
// RUNS IN NO-MALLOC CONTEXT
uintptr_t Value() const { return mValue; }
// RUNS IN NO-MALLOC CONTEXT
bool Valid() const { return mValid; }
private:
uintptr_t mValue;
bool mValid;
};
// The registers, with validity tags, that will be unwound.
struct UnwindRegs {
#if defined(LUL_ARCH_arm)
TaggedUWord r7;
TaggedUWord r11;
TaggedUWord r12;
TaggedUWord r13;
TaggedUWord r14;
TaggedUWord r15;
#elif defined(LUL_ARCH_x64) || defined(LUL_ARCH_x86)
TaggedUWord xbp;
TaggedUWord xsp;
TaggedUWord xip;
#else
# error "Unknown plat"
#endif
};
// The maximum number of bytes in a stack snapshot. This can be
// increased if necessary, but larger values cost performance, since a
// stack snapshot needs to be copied between sampling and worker
// threads for each snapshot. In practice 32k seems to be enough
// to get good backtraces.
static const size_t N_STACK_BYTES = 32768;
// The stack chunk image that will be unwound.
struct StackImage {
// [start_avma, +len) specify the address range in the buffer.
// Obviously we require 0 <= len <= N_STACK_BYTES.
uintptr_t mStartAvma;
size_t mLen;
uint8_t mContents[N_STACK_BYTES];
};
// Statistics collection for the unwinder.
template<typename T>
class LULStats {
public:
LULStats()
: mContext(0)
, mCFI(0)
, mScanned(0)
{}
template <typename S>
explicit LULStats(const LULStats<S>& aOther)
: mContext(aOther.mContext)
, mCFI(aOther.mCFI)
, mScanned(aOther.mScanned)
{}
template <typename S>
LULStats<T>& operator=(const LULStats<S>& aOther)
{
mContext = aOther.mContext;
mCFI = aOther.mCFI;
mScanned = aOther.mScanned;
return *this;
}
template <typename S>
uint32_t operator-(const LULStats<S>& aOther) {
return (mContext - aOther.mContext) +
(mCFI - aOther.mCFI) + (mScanned - aOther.mScanned);
}
T mContext; // Number of context frames
T mCFI; // Number of CFI/EXIDX frames
T mScanned; // Number of scanned frames
};
// The core unwinder library class. Just one of these is needed, and
// it can be shared by multiple unwinder threads.
//
// The library operates in one of two modes.
//
// * Admin mode. The library is this state after creation. In Admin
// mode, no unwinding may be performed. It is however allowable to
// perform administrative tasks -- primarily, loading of unwind info
// -- in this mode. In particular, it is safe for the library to
// perform dynamic memory allocation in this mode. Safe in the
// sense that there is no risk of deadlock against unwinding threads
// that might -- because of where they have been sampled -- hold the
// system's malloc lock.
//
// * Unwind mode. In this mode, calls to ::Unwind may be made, but
// nothing else. ::Unwind guarantees not to make any dynamic memory
// requests, so as to guarantee that the calling thread won't
// deadlock in the case where it already holds the system's malloc lock.
//
// The library is created in Admin mode. After debuginfo is loaded,
// the caller must switch it into Unwind mode by calling
// ::EnableUnwinding. There is no way to switch it back to Admin mode
// after that. To safely switch back to Admin mode would require the
// caller (or other external agent) to guarantee that there are no
// pending ::Unwind calls.
class PriMap;
class SegArray;
class UniqueStringUniverse;
class LUL {
public:
// Create; supply a logging sink. Sets the object in Admin mode.
explicit LUL(void (*aLog)(const char*));
// Destroy. Caller is responsible for ensuring that no other
// threads are in Unwind calls. All resources are freed and all
// registered unwinder threads are deregistered. Can be called
// either in Admin or Unwind mode.
~LUL();
// Notify the library that unwinding is now allowed and so
// admin-mode calls are no longer allowed. The object is initially
// created in admin mode. The only possible transition is
// admin->unwinding, therefore.
void EnableUnwinding();
// Notify of a new r-x mapping, and load the associated unwind info.
// The filename is strdup'd and used for debug printing. If
// aMappedImage is NULL, this function will mmap/munmap the file
// itself, so as to be able to read the unwind info. If
// aMappedImage is non-NULL then it is assumed to point to a
// called-supplied and caller-managed mapped image of the file.
// May only be called in Admin mode.
void NotifyAfterMap(uintptr_t aRXavma, size_t aSize,
const char* aFileName, const void* aMappedImage);
// In rare cases we know an executable area exists but don't know
// what the associated file is. This call notifies LUL of such
// areas. This is important for correct functioning of stack
// scanning and of the x86-{linux,android} special-case
// __kernel_syscall function handling.
// This must be called only after the code area in
// question really has been mapped.
// May only be called in Admin mode.
void NotifyExecutableArea(uintptr_t aRXavma, size_t aSize);
// Notify that a mapped area has been unmapped; discard any
// associated unwind info. Acquires mRWlock for writing. Note that
// to avoid segfaulting the stack-scan unwinder, which inspects code
// areas, this must be called before the code area in question is
// really unmapped. Note that, unlike NotifyAfterMap(), this
// function takes the start and end addresses of the range to be
// unmapped, rather than a start and a length parameter. This is so
// as to make it possible to notify an unmap for the entire address
// space using a single call.
// May only be called in Admin mode.
void NotifyBeforeUnmap(uintptr_t aAvmaMin, uintptr_t aAvmaMax);
// Apply NotifyBeforeUnmap to the entire address space. This causes
// LUL to discard all unwind and executable-area information for the
// entire address space.
// May only be called in Admin mode.
void NotifyBeforeUnmapAll() {
NotifyBeforeUnmap(0, UINTPTR_MAX);
}
// Returns the number of mappings currently registered.
// May only be called in Admin mode.
size_t CountMappings();
// Unwind |aStackImg| starting with the context in |aStartRegs|.
// Write the number of frames recovered in *aFramesUsed. Put
// the PC values in aFramePCs[0 .. *aFramesUsed-1] and
// the SP values in aFrameSPs[0 .. *aFramesUsed-1].
// |aFramesAvail| is the size of the two output arrays and hence the
// largest possible value of *aFramesUsed. PC values are always
// valid, and the unwind will stop when the PC becomes invalid, but
// the SP values might be invalid, in which case the value zero will
// be written in the relevant frameSPs[] slot.
//
// Unwinding may optionally use stack scanning. The maximum number
// of frames that may be recovered by stack scanning is
// |aScannedFramesAllowed| and the actual number recovered is
// written into *aScannedFramesAcquired. |aScannedFramesAllowed|
// must be less than or equal to |aFramesAvail|.
//
// This function assumes that the SP values increase as it unwinds
// away from the innermost frame -- that is, that the stack grows
// down. It monitors SP values as it unwinds to check they
// decrease, so as to avoid looping on corrupted stacks.
//
// May only be called in Unwind mode. Multiple threads may unwind
// at once. LUL user is responsible for ensuring that no thread makes
// any Admin calls whilst in Unwind mode.
// MOZ_CRASHes if the calling thread is not registered for unwinding.
//
// Up to aScannedFramesAllowed stack-scanned frames may be recovered.
//
// The calling thread must previously have been registered via a call to
// RegisterSampledThread.
void Unwind(/*OUT*/uintptr_t* aFramePCs,
/*OUT*/uintptr_t* aFrameSPs,
/*OUT*/size_t* aFramesUsed,
/*OUT*/size_t* aScannedFramesAcquired,
size_t aFramesAvail,
size_t aScannedFramesAllowed,
UnwindRegs* aStartRegs, StackImage* aStackImg);
// The logging sink. Call to send debug strings to the caller-
// specified destination. Can only be called by the Admin thread.
void (*mLog)(const char*);
// Statistics relating to unwinding. These have to be atomic since
// unwinding can occur on different threads simultaneously.
LULStats<mozilla::Atomic<uint32_t>> mStats;
// Possibly show the statistics. This may not be called from any
// registered sampling thread, since it involves I/O.
void MaybeShowStats();
private:
// The statistics counters at the point where they were last printed.
LULStats<uint32_t> mStatsPrevious;
// Are we in admin mode? Initially |true| but changes to |false|
// once unwinding begins.
bool mAdminMode;
// The thread ID associated with admin mode. This is the only thread
// that is allowed do perform non-Unwind calls on this object. Conversely,
// no registered Unwinding thread may be the admin thread. This is so
// as to clearly partition the one thread that may do dynamic memory
// allocation from the threads that are being sampled, since the latter
// absolutely may not do dynamic memory allocation.
int mAdminThreadId;
// The top level mapping from code address ranges to postprocessed
// unwind info. Basically a sorted array of (addr, len, info)
// records. This field is updated by NotifyAfterMap and NotifyBeforeUnmap.
PriMap* mPriMap;
// An auxiliary structure that records which address ranges are
// mapped r-x, for the benefit of the stack scanner.
SegArray* mSegArray;
// A UniqueStringUniverse that holds all the strdup'd strings created
// whilst reading unwind information. This is included so as to make
// it possible to free them in ~LUL.
UniqueStringUniverse* mUSU;
};
// Run unit tests on an initialised, loaded-up LUL instance, and print
// summary results on |aLUL|'s logging sink. Also return the number
// of tests run in *aNTests and the number that passed in
// *aNTestsPassed.
void
RunLulUnitTests(/*OUT*/int* aNTests, /*OUT*/int*aNTestsPassed, LUL* aLUL);
} // namespace lul
#endif // LulMain_h

View file

@ -1,88 +0,0 @@
/* -*- 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 <stdio.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "platform.h"
#include "PlatformMacros.h"
#include "LulMain.h"
#include "shared-libraries.h"
#include "AutoObjectMapper.h"
// Contains miscellaneous helpers that are used to connect SPS and LUL.
// Find out, in a platform-dependent way, where the code modules got
// mapped in the process' virtual address space, and get |aLUL| to
// load unwind info for them.
void
read_procmaps(lul::LUL* aLUL)
{
MOZ_ASSERT(aLUL->CountMappings() == 0);
# if defined(SPS_OS_linux) || defined(SPS_OS_android) || defined(SPS_OS_darwin)
SharedLibraryInfo info = SharedLibraryInfo::GetInfoForSelf();
for (size_t i = 0; i < info.GetSize(); i++) {
const SharedLibrary& lib = info.GetEntry(i);
# if defined(SPS_OS_android)
// We're using faulty.lib. Use a special-case object mapper.
AutoObjectMapperFaultyLib mapper(aLUL->mLog);
# else
// We can use the standard POSIX-based mapper.
AutoObjectMapperPOSIX mapper(aLUL->mLog);
# endif
// Ask |mapper| to map the object. Then hand its mapped address
// to NotifyAfterMap().
void* image = nullptr;
size_t size = 0;
bool ok = mapper.Map(&image, &size, lib.GetName());
if (ok && image && size > 0) {
aLUL->NotifyAfterMap(lib.GetStart(), lib.GetEnd()-lib.GetStart(),
lib.GetName().c_str(), image);
} else if (!ok && lib.GetName() == "") {
// The object has no name and (as a consequence) the mapper
// failed to map it. This happens on Linux, where
// GetInfoForSelf() produces two such mappings: one for the
// executable and one for the VDSO. The executable one isn't a
// big deal since there's not much interesting code in there,
// but the VDSO one is a problem on x86-{linux,android} because
// lack of knowledge about the mapped area inhibits LUL's
// special __kernel_syscall handling. Hence notify |aLUL| at
// least of the mapping, even though it can't read any unwind
// information for the area.
aLUL->NotifyExecutableArea(lib.GetStart(), lib.GetEnd()-lib.GetStart());
}
// |mapper| goes out of scope at this point and so its destructor
// unmaps the object.
}
# else
# error "Unknown platform"
# endif
}
// LUL needs a callback for its logging sink.
void
logging_sink_for_LUL(const char* str) {
// Ignore any trailing \n, since LOG will add one anyway.
size_t n = strlen(str);
if (n > 0 && str[n-1] == '\n') {
char* tmp = strdup(str);
tmp[n-1] = 0;
LOG(tmp);
free(tmp);
} else {
LOG(str);
}
}

View file

@ -1,24 +0,0 @@
/* -*- 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/. */
#ifndef MOZ_PLATFORM_LINUX_LUL_H
#define MOZ_PLATFORM_LINUX_LUL_H
#include "platform.h"
// Find out, in a platform-dependent way, where the code modules got
// mapped in the process' virtual address space, and get |aLUL| to
// load unwind info for them.
void
read_procmaps(lul::LUL* aLUL);
// LUL needs a callback for its logging sink.
void
logging_sink_for_LUL(const char* str);
// A singleton instance of the library.
extern lul::LUL* sLUL;
#endif /* ndef MOZ_PLATFORM_LINUX_LUL_H */

View file

@ -4,112 +4,6 @@
# 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/.
if CONFIG['MOZ_ENABLE_PROFILER_SPS']:
XPIDL_MODULE = 'profiler'
XPIDL_SOURCES += [
'gecko/nsIProfiler.idl',
'gecko/nsIProfileSaveEvent.idl',
]
EXPORTS += [
'public/GeckoProfilerFunc.h',
'public/GeckoProfilerImpl.h',
'public/ProfilerBacktrace.h',
'public/ProfilerMarkers.h',
'public/PseudoStack.h',
'public/shared-libraries.h',
]
EXPORTS.mozilla += [
'public/ProfileGatherer.h',
]
EXTRA_JS_MODULES += [
'gecko/Profiler.jsm',
]
UNIFIED_SOURCES += [
'core/GeckoSampler.cpp',
'core/platform.cpp',
'core/ProfileBuffer.cpp',
'core/ProfileEntry.cpp',
'core/ProfileJSONWriter.cpp',
'core/ProfilerBacktrace.cpp',
'core/ProfilerMarkers.cpp',
'core/StackTop.cpp',
'core/SyncProfile.cpp',
'core/ThreadInfo.cpp',
'core/ThreadProfile.cpp',
'gecko/nsProfiler.cpp',
'gecko/nsProfilerFactory.cpp',
'gecko/nsProfilerStartParams.cpp',
'gecko/ProfileGatherer.cpp',
'gecko/ProfilerIOInterposeObserver.cpp',
'gecko/SaveProfileTask.cpp',
'gecko/ThreadResponsiveness.cpp',
]
if CONFIG['OS_TARGET'] in ('Android', 'Linux'):
UNIFIED_SOURCES += [
'lul/AutoObjectMapper.cpp',
'lul/LulCommon.cpp',
'lul/LulDwarf.cpp',
'lul/LulDwarfSummariser.cpp',
'lul/LulElf.cpp',
'lul/LulMain.cpp',
'lul/platform-linux-lul.cpp',
]
# These files cannot be built in unified mode because of name clashes with mozglue headers on Android.
SOURCES += [
'core/platform-linux.cc',
'core/shared-libraries-linux.cc',
]
SOURCES += [
'/toolkit/crashreporter/google-breakpad/src/common/linux/elfutils.cc',
'/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc',
'/toolkit/crashreporter/google-breakpad/src/common/linux/guid_creator.cc',
'/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.cc',
'/toolkit/crashreporter/google-breakpad/src/common/linux/memory_mapped_file.cc',
]
if CONFIG['CPU_ARCH'] == 'arm':
SOURCES += [
'core/EHABIStackWalk.cpp',
]
elif CONFIG['OS_TARGET'] == 'Darwin':
UNIFIED_SOURCES += [
'core/platform-macos.cc',
'core/shared-libraries-macos.cc',
]
elif CONFIG['OS_TARGET'] == 'WINNT':
SOURCES += [
'core/IntelPowerGadget.cpp',
'core/platform-win32.cc',
'core/shared-libraries-win32.cc',
]
LOCAL_INCLUDES += [
'/docshell/base',
'/ipc/chromium/src',
'/mozglue/linker',
'/toolkit/crashreporter/google-breakpad/src',
'/tools/profiler/core/',
'/tools/profiler/gecko/',
'/xpcom/base',
]
if CONFIG['OS_TARGET'] == 'Android':
LOCAL_INCLUDES += [
# We need access to Breakpad's getcontext(3) which is suitable for Android
'/toolkit/crashreporter/google-breakpad/src/common/android/include',
]
if CONFIG['OS_TARGET'] == 'Android':
SOURCES += ['/toolkit/crashreporter/google-breakpad/src/common/android/breakpad_getcontext.S']
if CONFIG['ANDROID_CPU_ARCH'] == 'armeabi':
DEFINES['ARCH_ARMV6'] = True
if CONFIG['ENABLE_TESTS']:
DIRS += ['tests/gtest']
FINAL_LIBRARY = 'xul'
IPDL_SOURCES += [
'gecko/ProfilerTypes.ipdlh',
]

View file

@ -79,8 +79,6 @@ enum TracingMetadata {
TRACING_TIMESTAMP
};
#if !defined(MOZ_ENABLE_PROFILER_SPS)
#include <stdint.h>
#include <stdarg.h>
@ -248,12 +246,6 @@ static inline bool profiler_in_privacy_mode() { return false; }
static inline void profiler_log(const char *str) {}
static inline void profiler_log(const char *fmt, va_list args) {}
#else
#include "GeckoProfilerImpl.h"
#endif
class MOZ_RAII GeckoProfilerInitRAII {
public:
explicit GeckoProfilerInitRAII(void* stackTop) {

View file

@ -1,125 +0,0 @@
/* -*- 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/. */
#ifndef PROFILER_FUNCS_H
#define PROFILER_FUNCS_H
#ifndef SPS_STANDALONE
#include "js/TypeDecls.h"
#endif
#include "js/ProfilingStack.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Vector.h"
#include <stdint.h>
class nsISupports;
namespace mozilla {
class TimeStamp;
namespace dom {
class Promise;
} // namespace dom
} // namespace mozilla
class ProfilerBacktrace;
class ProfilerMarkerPayload;
// Returns a handle to pass on exit. This can check that we are popping the
// correct callstack.
inline void* mozilla_sampler_call_enter(const char *aInfo, js::ProfileEntry::Category aCategory,
void *aFrameAddress = nullptr, bool aCopy = false,
uint32_t line = 0);
inline void mozilla_sampler_call_exit(void* handle);
void mozilla_sampler_add_marker(const char *aInfo,
ProfilerMarkerPayload *aPayload = nullptr);
void mozilla_sampler_start(int aEntries, double aInterval,
const char** aFeatures, uint32_t aFeatureCount,
const char** aThreadNameFilters, uint32_t aFilterCount);
void mozilla_sampler_stop();
bool mozilla_sampler_is_paused();
void mozilla_sampler_pause();
void mozilla_sampler_resume();
ProfilerBacktrace* mozilla_sampler_get_backtrace();
void mozilla_sampler_free_backtrace(ProfilerBacktrace* aBacktrace);
void mozilla_sampler_get_backtrace_noalloc(char *output, size_t outputSize);
bool mozilla_sampler_is_active();
bool mozilla_sampler_feature_active(const char* aName);
void mozilla_sampler_responsiveness(const mozilla::TimeStamp& time);
void mozilla_sampler_frame_number(int frameNumber);
const double* mozilla_sampler_get_responsiveness();
void mozilla_sampler_save();
mozilla::UniquePtr<char[]> mozilla_sampler_get_profile(double aSinceTime);
#ifndef SPS_STANDALONE
JSObject *mozilla_sampler_get_profile_data(JSContext* aCx, double aSinceTime);
void mozilla_sampler_get_profile_data_async(double aSinceTime,
mozilla::dom::Promise* aPromise);
void mozilla_sampler_get_profiler_start_params(int* aEntrySize,
double* aInterval,
mozilla::Vector<const char*>* aFilters,
mozilla::Vector<const char*>* aFeatures);
void mozilla_sampler_get_gatherer(nsISupports** aRetVal);
#endif
// Make this function easily callable from a debugger in a build without
// debugging information (work around http://llvm.org/bugs/show_bug.cgi?id=22211)
extern "C" {
void mozilla_sampler_save_profile_to_file(const char* aFilename);
}
const char** mozilla_sampler_get_features();
void mozilla_sampler_get_buffer_info(uint32_t *aCurrentPosition, uint32_t *aTotalSize,
uint32_t *aGeneration);
void mozilla_sampler_init(void* stackTop);
void mozilla_sampler_shutdown();
// Lock the profiler. When locked the profiler is (1) stopped,
// (2) profile data is cleared, (3) profiler-locked is fired.
// This is used to lock down the profiler during private browsing
void mozilla_sampler_lock();
// Unlock the profiler, leaving it stopped and fires profiler-unlocked.
void mozilla_sampler_unlock();
// Register/unregister threads with the profiler
bool mozilla_sampler_register_thread(const char* name, void* stackTop);
void mozilla_sampler_unregister_thread();
void mozilla_sampler_sleep_start();
void mozilla_sampler_sleep_end();
bool mozilla_sampler_is_sleeping();
double mozilla_sampler_time();
double mozilla_sampler_time(const mozilla::TimeStamp& aTime);
void mozilla_sampler_tracing(const char* aCategory, const char* aInfo,
TracingMetadata aMetaData);
void mozilla_sampler_tracing(const char* aCategory, const char* aInfo,
ProfilerBacktrace* aCause,
TracingMetadata aMetaData);
void mozilla_sampler_log(const char *fmt, va_list args);
#endif

View file

@ -1,515 +0,0 @@
/* -*- 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/. */
// IWYU pragma: private, include "GeckoProfiler.h"
#ifndef TOOLS_SPS_SAMPLER_H_
#define TOOLS_SPS_SAMPLER_H_
#include <stdlib.h>
#include <signal.h>
#include <stdarg.h>
#include "mozilla/Assertions.h"
#include "mozilla/GuardObjects.h"
#include "mozilla/Sprintf.h"
#include "mozilla/ThreadLocal.h"
#include "mozilla/UniquePtr.h"
#ifndef SPS_STANDALONE
#include "nscore.h"
#include "nsISupports.h"
#endif
#include "GeckoProfilerFunc.h"
#include "PseudoStack.h"
#include "ProfilerBacktrace.h"
// Make sure that we can use std::min here without the Windows headers messing with us.
#ifdef min
#undef min
#endif
class GeckoSampler;
namespace mozilla {
class TimeStamp;
} // namespace mozilla
extern MOZ_THREAD_LOCAL(PseudoStack *) tlsPseudoStack;
extern MOZ_THREAD_LOCAL(GeckoSampler *) tlsTicker;
extern MOZ_THREAD_LOCAL(void *) tlsStackTop;
extern bool stack_key_initialized;
#ifndef SAMPLE_FUNCTION_NAME
# ifdef __GNUC__
# define SAMPLE_FUNCTION_NAME __FUNCTION__
# elif defined(_MSC_VER)
# define SAMPLE_FUNCTION_NAME __FUNCTION__
# else
# define SAMPLE_FUNCTION_NAME __func__ // defined in C99, supported in various C++ compilers. Just raw function name.
# endif
#endif
static inline
void profiler_init(void* stackTop)
{
mozilla_sampler_init(stackTop);
}
static inline
void profiler_shutdown()
{
mozilla_sampler_shutdown();
}
static inline
void profiler_start(int aProfileEntries, double aInterval,
const char** aFeatures, uint32_t aFeatureCount,
const char** aThreadNameFilters, uint32_t aFilterCount)
{
mozilla_sampler_start(aProfileEntries, aInterval, aFeatures, aFeatureCount, aThreadNameFilters, aFilterCount);
}
static inline
void profiler_stop()
{
mozilla_sampler_stop();
}
static inline
bool profiler_is_paused()
{
return mozilla_sampler_is_paused();
}
static inline
void profiler_pause()
{
mozilla_sampler_pause();
}
static inline
void profiler_resume()
{
mozilla_sampler_resume();
}
static inline
ProfilerBacktrace* profiler_get_backtrace()
{
return mozilla_sampler_get_backtrace();
}
static inline
void profiler_free_backtrace(ProfilerBacktrace* aBacktrace)
{
mozilla_sampler_free_backtrace(aBacktrace);
}
static inline
void profiler_get_backtrace_noalloc(char *output, size_t outputSize)
{
return mozilla_sampler_get_backtrace_noalloc(output, outputSize);
}
static inline
bool profiler_is_active()
{
return mozilla_sampler_is_active();
}
static inline
bool profiler_feature_active(const char* aName)
{
return mozilla_sampler_feature_active(aName);
}
static inline
void profiler_responsiveness(const mozilla::TimeStamp& aTime)
{
mozilla_sampler_responsiveness(aTime);
}
static inline
void profiler_set_frame_number(int frameNumber)
{
return mozilla_sampler_frame_number(frameNumber);
}
static inline
mozilla::UniquePtr<char[]> profiler_get_profile(double aSinceTime = 0)
{
return mozilla_sampler_get_profile(aSinceTime);
}
#ifndef SPS_STANDALONE
static inline
JSObject* profiler_get_profile_jsobject(JSContext* aCx, double aSinceTime = 0)
{
return mozilla_sampler_get_profile_data(aCx, aSinceTime);
}
static inline
void profiler_get_profile_jsobject_async(double aSinceTime = 0,
mozilla::dom::Promise* aPromise = 0)
{
mozilla_sampler_get_profile_data_async(aSinceTime, aPromise);
}
static inline
void profiler_get_start_params(int* aEntrySize,
double* aInterval,
mozilla::Vector<const char*>* aFilters,
mozilla::Vector<const char*>* aFeatures)
{
mozilla_sampler_get_profiler_start_params(aEntrySize, aInterval, aFilters, aFeatures);
}
static inline
void profiler_get_gatherer(nsISupports** aRetVal)
{
mozilla_sampler_get_gatherer(aRetVal);
}
#endif
static inline
void profiler_save_profile_to_file(const char* aFilename)
{
return mozilla_sampler_save_profile_to_file(aFilename);
}
static inline
const char** profiler_get_features()
{
return mozilla_sampler_get_features();
}
static inline
void profiler_get_buffer_info(uint32_t *aCurrentPosition, uint32_t *aTotalSize,
uint32_t *aGeneration)
{
return mozilla_sampler_get_buffer_info(aCurrentPosition, aTotalSize, aGeneration);
}
static inline
void profiler_lock()
{
return mozilla_sampler_lock();
}
static inline
void profiler_unlock()
{
return mozilla_sampler_unlock();
}
static inline
void profiler_register_thread(const char* name, void* guessStackTop)
{
mozilla_sampler_register_thread(name, guessStackTop);
}
static inline
void profiler_unregister_thread()
{
mozilla_sampler_unregister_thread();
}
static inline
void profiler_sleep_start()
{
mozilla_sampler_sleep_start();
}
static inline
void profiler_sleep_end()
{
mozilla_sampler_sleep_end();
}
static inline
bool profiler_is_sleeping()
{
return mozilla_sampler_is_sleeping();
}
#ifndef SPS_STANDALONE
static inline
void profiler_js_operation_callback()
{
PseudoStack *stack = tlsPseudoStack.get();
if (!stack) {
return;
}
stack->jsOperationCallback();
}
#endif
static inline
double profiler_time()
{
return mozilla_sampler_time();
}
static inline
double profiler_time(const mozilla::TimeStamp& aTime)
{
return mozilla_sampler_time(aTime);
}
static inline
bool profiler_in_privacy_mode()
{
PseudoStack *stack = tlsPseudoStack.get();
if (!stack) {
return false;
}
return stack->mPrivacyMode;
}
static inline void profiler_tracing(const char* aCategory, const char* aInfo,
ProfilerBacktrace* aCause,
TracingMetadata aMetaData = TRACING_DEFAULT)
{
// Don't insert a marker if we're not profiling to avoid
// the heap copy (malloc).
if (!stack_key_initialized || !profiler_is_active()) {
delete aCause;
return;
}
mozilla_sampler_tracing(aCategory, aInfo, aCause, aMetaData);
}
static inline void profiler_tracing(const char* aCategory, const char* aInfo,
TracingMetadata aMetaData = TRACING_DEFAULT)
{
if (!stack_key_initialized)
return;
// Don't insert a marker if we're not profiling to avoid
// the heap copy (malloc).
if (!profiler_is_active()) {
return;
}
mozilla_sampler_tracing(aCategory, aInfo, aMetaData);
}
#define SAMPLER_APPEND_LINE_NUMBER_PASTE(id, line) id ## line
#define SAMPLER_APPEND_LINE_NUMBER_EXPAND(id, line) SAMPLER_APPEND_LINE_NUMBER_PASTE(id, line)
#define SAMPLER_APPEND_LINE_NUMBER(id) SAMPLER_APPEND_LINE_NUMBER_EXPAND(id, __LINE__)
// Uncomment this to turn on systrace or build with
// ac_add_options --enable-systace
//#define MOZ_USE_SYSTRACE
#ifdef MOZ_USE_SYSTRACE
#ifndef ATRACE_TAG
# define ATRACE_TAG ATRACE_TAG_ALWAYS
#endif
// We need HAVE_ANDROID_OS to be defined for Trace.h.
// If its not set we will set it temporary and remove it.
# ifndef HAVE_ANDROID_OS
# define HAVE_ANDROID_OS
# define REMOVE_HAVE_ANDROID_OS
# endif
// Android source code will include <cutils/trace.h> before this. There is no
// HAVE_ANDROID_OS defined in Firefox OS build at that time. Enabled it globally
// will cause other build break. So atrace_begin and atrace_end are not defined.
// It will cause a build-break when we include <utils/Trace.h>. Use undef
// _LIBS_CUTILS_TRACE_H will force <cutils/trace.h> to define atrace_begin and
// atrace_end with defined HAVE_ANDROID_OS again. Then there is no build-break.
# undef _LIBS_CUTILS_TRACE_H
# include <utils/Trace.h>
# define MOZ_PLATFORM_TRACING(name) android::ScopedTrace SAMPLER_APPEND_LINE_NUMBER(scopedTrace)(ATRACE_TAG, name);
# ifdef REMOVE_HAVE_ANDROID_OS
# undef HAVE_ANDROID_OS
# undef REMOVE_HAVE_ANDROID_OS
# endif
#else
# define MOZ_PLATFORM_TRACING(name)
#endif
// we want the class and function name but can't easily get that using preprocessor macros
// __func__ doesn't have the class name and __PRETTY_FUNCTION__ has the parameters
#define PROFILER_LABEL(name_space, info, category) MOZ_PLATFORM_TRACING(name_space "::" info) mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, category, __LINE__)
#define PROFILER_LABEL_FUNC(category) MOZ_PLATFORM_TRACING(SAMPLE_FUNCTION_NAME) mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(SAMPLE_FUNCTION_NAME, category, __LINE__)
#define PROFILER_LABEL_PRINTF(name_space, info, category, ...) MOZ_PLATFORM_TRACING(name_space "::" info) mozilla::SamplerStackFramePrintfRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, category, __LINE__, __VA_ARGS__)
#define PROFILER_MARKER(info) mozilla_sampler_add_marker(info)
#define PROFILER_MARKER_PAYLOAD(info, payload) mozilla_sampler_add_marker(info, payload)
#define PROFILER_MAIN_THREAD_MARKER(info) MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla_sampler_add_marker(info)
#define PROFILER_MAIN_THREAD_LABEL(name_space, info, category) MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla::SamplerStackFrameRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, category, __LINE__)
#define PROFILER_MAIN_THREAD_LABEL_PRINTF(name_space, info, category, ...) MOZ_ASSERT(NS_IsMainThread(), "This can only be called on the main thread"); mozilla::SamplerStackFramePrintfRAII SAMPLER_APPEND_LINE_NUMBER(sampler_raii)(name_space "::" info, category, __LINE__, __VA_ARGS__)
#if !defined(PLATFORM_LIKELY_MEMORY_CONSTRAINED) && !defined(ARCH_ARMV6)
# define PROFILE_DEFAULT_ENTRY 1000000
#else
# define PROFILE_DEFAULT_ENTRY 100000
#endif
// In the case of profiler_get_backtrace we know that we only need enough space
// for a single backtrace.
#define GET_BACKTRACE_DEFAULT_ENTRY 1000
#if defined(PLATFORM_LIKELY_MEMORY_CONSTRAINED)
/* A 1ms sampling interval has been shown to be a large perf hit
* (10fps) on memory-contrained (low-end) platforms, and additionally
* to yield different results from the profiler. Where this is the
* important case, b2g, there are also many gecko processes which
* magnify these effects. */
# define PROFILE_DEFAULT_INTERVAL 10
#elif defined(ANDROID)
// We use a lower frequency on Android, in order to make things work
// more smoothly on phones. This value can be adjusted later with
// some libunwind optimizations.
// In one sample measurement on Galaxy Nexus, out of about 700 backtraces,
// 60 of them took more than 25ms, and the average and standard deviation
// were 6.17ms and 9.71ms respectively.
// For now since we don't support stackwalking let's use 1ms since it's fast
// enough.
#define PROFILE_DEFAULT_INTERVAL 1
#else
#define PROFILE_DEFAULT_INTERVAL 1
#endif
#define PROFILE_DEFAULT_FEATURES NULL
#define PROFILE_DEFAULT_FEATURE_COUNT 0
namespace mozilla {
class MOZ_RAII GeckoProfilerTracingRAII {
public:
GeckoProfilerTracingRAII(const char* aCategory, const char* aInfo,
mozilla::UniquePtr<ProfilerBacktrace> aBacktrace
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: mCategory(aCategory)
, mInfo(aInfo)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
profiler_tracing(mCategory, mInfo, aBacktrace.release(), TRACING_INTERVAL_START);
}
~GeckoProfilerTracingRAII() {
profiler_tracing(mCategory, mInfo, TRACING_INTERVAL_END);
}
protected:
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
const char* mCategory;
const char* mInfo;
};
class MOZ_RAII SamplerStackFrameRAII {
public:
// we only copy the strings at save time, so to take multiple parameters we'd need to copy them then.
SamplerStackFrameRAII(const char *aInfo,
js::ProfileEntry::Category aCategory, uint32_t line
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
mHandle = mozilla_sampler_call_enter(aInfo, aCategory, this, false, line);
}
~SamplerStackFrameRAII() {
mozilla_sampler_call_exit(mHandle);
}
private:
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
void* mHandle;
};
static const int SAMPLER_MAX_STRING = 128;
class MOZ_RAII SamplerStackFramePrintfRAII {
public:
// we only copy the strings at save time, so to take multiple parameters we'd need to copy them then.
SamplerStackFramePrintfRAII(const char *aInfo,
js::ProfileEntry::Category aCategory, uint32_t line, const char *aFormat, ...)
: mHandle(nullptr)
{
if (profiler_is_active() && !profiler_in_privacy_mode()) {
va_list args;
va_start(args, aFormat);
char buff[SAMPLER_MAX_STRING];
// We have to use seperate printf's because we're using
// the vargs.
VsprintfLiteral(buff, aFormat, args);
SprintfLiteral(mDest, "%s %s", aInfo, buff);
mHandle = mozilla_sampler_call_enter(mDest, aCategory, this, true, line);
va_end(args);
} else {
mHandle = mozilla_sampler_call_enter(aInfo, aCategory, this, false, line);
}
}
~SamplerStackFramePrintfRAII() {
mozilla_sampler_call_exit(mHandle);
}
private:
char mDest[SAMPLER_MAX_STRING];
void* mHandle;
};
} // namespace mozilla
inline PseudoStack* mozilla_get_pseudo_stack(void)
{
if (!stack_key_initialized)
return nullptr;
return tlsPseudoStack.get();
}
inline void* mozilla_sampler_call_enter(const char *aInfo,
js::ProfileEntry::Category aCategory, void *aFrameAddress, bool aCopy, uint32_t line)
{
// check if we've been initialized to avoid calling pthread_getspecific
// with a null tlsStack which will return undefined results.
if (!stack_key_initialized)
return nullptr;
PseudoStack *stack = tlsPseudoStack.get();
// we can't infer whether 'stack' has been initialized
// based on the value of stack_key_intiailized because
// 'stack' is only intialized when a thread is being
// profiled.
if (!stack) {
return stack;
}
stack->push(aInfo, aCategory, aFrameAddress, aCopy, line);
// The handle is meant to support future changes
// but for now it is simply use to save a call to
// pthread_getspecific on exit. It also supports the
// case where the sampler is initialized between
// enter and exit.
return stack;
}
inline void mozilla_sampler_call_exit(void *aHandle)
{
if (!aHandle)
return;
PseudoStack *stack = (PseudoStack*)aHandle;
stack->popAndMaybeDelete();
}
void mozilla_sampler_add_marker(const char *aMarker, ProfilerMarkerPayload *aPayload);
static inline
void profiler_log(const char *str)
{
profiler_tracing("log", str, TRACING_EVENT);
}
static inline
void profiler_log(const char *fmt, va_list args)
{
mozilla_sampler_log(fmt, args);
}
#endif /* ndef TOOLS_SPS_SAMPLER_H_ */

View file

@ -1,42 +0,0 @@
/* 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/. */
#ifndef MOZ_PROFILE_GATHERER_H
#define MOZ_PROFILE_GATHERER_H
#include "mozilla/dom/Promise.h"
class GeckoSampler;
namespace mozilla {
class ProfileGatherer final : public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
explicit ProfileGatherer(GeckoSampler* aTicker);
void WillGatherOOPProfile();
void GatheredOOPProfile();
void Start(double aSinceTime, mozilla::dom::Promise* aPromise);
void Cancel();
void OOPExitProfile(const nsCString& aProfile);
private:
~ProfileGatherer() {};
void Finish();
void Reset();
nsTArray<nsCString> mExitProfiles;
RefPtr<mozilla::dom::Promise> mPromise;
GeckoSampler* mTicker;
double mSinceTime;
uint32_t mPendingProfiles;
bool mGathering;
};
} // namespace mozilla
#endif

View file

@ -1,36 +0,0 @@
/* -*- Mode: C++; tab-width: 2; 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/. */
#ifndef __PROFILER_BACKTRACE_H
#define __PROFILER_BACKTRACE_H
class SyncProfile;
class SpliceableJSONWriter;
class UniqueStacks;
class ProfilerBacktrace
{
public:
explicit ProfilerBacktrace(SyncProfile* aProfile);
~ProfilerBacktrace();
// ProfilerBacktraces' stacks are deduplicated in the context of the
// profile that contains the backtrace as a marker payload.
//
// That is, markers that contain backtraces should not need their own stack,
// frame, and string tables. They should instead reuse their parent
// profile's tables.
void StreamJSON(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks);
private:
ProfilerBacktrace(const ProfilerBacktrace&);
ProfilerBacktrace& operator=(const ProfilerBacktrace&);
SyncProfile* mProfile;
};
#endif // __PROFILER_BACKTRACE_H

View file

@ -1,193 +0,0 @@
/* -*- 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/. */
#ifndef PROFILER_MARKERS_H
#define PROFILER_MARKERS_H
#include "mozilla/TimeStamp.h"
#include "mozilla/Attributes.h"
namespace mozilla {
namespace layers {
class Layer;
} // namespace layers
} // namespace mozilla
class SpliceableJSONWriter;
class UniqueStacks;
/**
* This is an abstract object that can be implied to supply
* data to be attached with a profiler marker. Most data inserted
* into a profile is stored in a circular buffer. This buffer
* typically wraps around and overwrites most entries. Because
* of this, this structure is designed to defer the work of
* prepare the payload only when 'preparePayload' is called.
*
* Note when implementing that this object is typically constructed
* on a particular thread but 'preparePayload' and the destructor
* is called from the main thread.
*/
class ProfilerMarkerPayload
{
public:
/**
* ProfilerMarkerPayload takes ownership of aStack
*/
explicit ProfilerMarkerPayload(ProfilerBacktrace* aStack = nullptr);
ProfilerMarkerPayload(const mozilla::TimeStamp& aStartTime,
const mozilla::TimeStamp& aEndTime,
ProfilerBacktrace* aStack = nullptr);
/**
* Called from the main thread
*/
virtual ~ProfilerMarkerPayload();
/**
* Called from the main thread
*/
virtual void StreamPayload(SpliceableJSONWriter& aWriter,
UniqueStacks& aUniqueStacks) = 0;
mozilla::TimeStamp GetStartTime() const { return mStartTime; }
protected:
/**
* Called from the main thread
*/
void streamCommonProps(const char* aMarkerType, SpliceableJSONWriter& aWriter,
UniqueStacks& aUniqueStacks);
void SetStack(ProfilerBacktrace* aStack) { mStack = aStack; }
private:
mozilla::TimeStamp mStartTime;
mozilla::TimeStamp mEndTime;
ProfilerBacktrace* mStack;
};
class ProfilerMarkerTracing : public ProfilerMarkerPayload
{
public:
ProfilerMarkerTracing(const char* aCategory, TracingMetadata aMetaData);
ProfilerMarkerTracing(const char* aCategory, TracingMetadata aMetaData, ProfilerBacktrace* aCause);
const char *GetCategory() const { return mCategory; }
TracingMetadata GetMetaData() const { return mMetaData; }
virtual void StreamPayload(SpliceableJSONWriter& aWriter,
UniqueStacks& aUniqueStacks) override;
private:
const char *mCategory;
TracingMetadata mMetaData;
};
#ifndef SPS_STANDALONE
#include "gfxASurface.h"
class ProfilerMarkerImagePayload : public ProfilerMarkerPayload
{
public:
explicit ProfilerMarkerImagePayload(gfxASurface *aImg);
virtual void StreamPayload(SpliceableJSONWriter& aWriter,
UniqueStacks& aUniqueStacks) override;
private:
RefPtr<gfxASurface> mImg;
};
class IOMarkerPayload : public ProfilerMarkerPayload
{
public:
IOMarkerPayload(const char* aSource, const char* aFilename, const mozilla::TimeStamp& aStartTime,
const mozilla::TimeStamp& aEndTime,
ProfilerBacktrace* aStack);
~IOMarkerPayload();
virtual void StreamPayload(SpliceableJSONWriter& aWriter,
UniqueStacks& aUniqueStacks) override;
private:
const char* mSource;
char* mFilename;
};
/**
* Contains the translation applied to a 2d layer so we can
* track the layer position at each frame.
*/
class LayerTranslationPayload : public ProfilerMarkerPayload
{
public:
LayerTranslationPayload(mozilla::layers::Layer* aLayer,
mozilla::gfx::Point aPoint);
virtual void StreamPayload(SpliceableJSONWriter& aWriter,
UniqueStacks& aUniqueStacks) override;
private:
mozilla::layers::Layer* mLayer;
mozilla::gfx::Point mPoint;
};
#include "Units.h" // For ScreenIntPoint
/**
* Tracks when touch events are processed by gecko, not when
* the touch actually occured in gonk/android.
*/
class TouchDataPayload : public ProfilerMarkerPayload
{
public:
explicit TouchDataPayload(const mozilla::ScreenIntPoint& aPoint);
virtual ~TouchDataPayload() {}
virtual void StreamPayload(SpliceableJSONWriter& aWriter,
UniqueStacks& aUniqueStacks) override;
private:
mozilla::ScreenIntPoint mPoint;
};
/**
* Tracks when a vsync occurs according to the HardwareComposer.
*/
class VsyncPayload : public ProfilerMarkerPayload
{
public:
explicit VsyncPayload(mozilla::TimeStamp aVsyncTimestamp);
virtual ~VsyncPayload() {}
virtual void StreamPayload(SpliceableJSONWriter& aWriter,
UniqueStacks& aUniqueStacks) override;
private:
mozilla::TimeStamp mVsyncTimestamp;
};
class GPUMarkerPayload : public ProfilerMarkerPayload
{
public:
GPUMarkerPayload(const mozilla::TimeStamp& aCpuTimeStart,
const mozilla::TimeStamp& aCpuTimeEnd,
uint64_t aGpuTimeStart,
uint64_t aGpuTimeEnd);
~GPUMarkerPayload() {}
virtual void StreamPayload(SpliceableJSONWriter& aWriter,
UniqueStacks& aUniqueStacks) override;
private:
mozilla::TimeStamp mCpuTimeStart;
mozilla::TimeStamp mCpuTimeEnd;
uint64_t mGpuTimeStart;
uint64_t mGpuTimeEnd;
};
#endif
#endif // PROFILER_MARKERS_H

Some files were not shown because too many files have changed in this diff Show more