From 1171e866cf05ee3120fe674b3d33e81e44a40162 Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Thu, 2 Feb 2012 16:57:20 -0500 Subject: [PATCH] Bug 723711. Return the profile data as JS objects. r=bgirard --- tools/profiler/JSObjectBuilder.h | 153 +++++++++++++++++++++++++++++++ tools/profiler/TableTicker.cpp | 55 +++++++++++ tools/profiler/nsIProfiler.idl | 2 + tools/profiler/nsProfiler.cpp | 13 +++ tools/profiler/sampler.h | 1 + tools/profiler/sps_sampler.h | 3 + 6 files changed, 227 insertions(+) create mode 100644 tools/profiler/JSObjectBuilder.h diff --git a/tools/profiler/JSObjectBuilder.h b/tools/profiler/JSObjectBuilder.h new file mode 100644 index 000000000000..d2edc6f89d34 --- /dev/null +++ b/tools/profiler/JSObjectBuilder.h @@ -0,0 +1,153 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2012 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jeff Muizelaar + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "jsapi.h" + +/* this is handy wrapper around JSAPI to make it more pleasant to use. + * We collect the JSAPI errors and so that callers don't need to */ +class JSObjectBuilder +{ + public: + + void DefineProperty(JSObject *aObject, const char *name, JSObject *aValue) + { + if (!mOk) + return; + + mOk = JS_DefineProperty(mCx, aObject, name, OBJECT_TO_JSVAL(aValue), NULL, NULL, JSPROP_ENUMERATE); + } + + void DefineProperty(JSObject *aObject, const char *name, int value) + { + if (!mOk) + return; + + mOk = JS_DefineProperty(mCx, aObject, name, INT_TO_JSVAL(value), NULL, NULL, JSPROP_ENUMERATE); + } + + void DefineProperty(JSObject *aObject, const char *name, double value) + { + if (!mOk) + return; + + mOk = JS_DefineProperty(mCx, aObject, name, DOUBLE_TO_JSVAL(value), NULL, NULL, JSPROP_ENUMERATE); + } + + void DefineProperty(JSObject *aObject, const char *name, nsAString &value) + { + if (!mOk) + return; + + const nsString &flat = PromiseFlatString(value); + JSString *string = JS_NewUCStringCopyN(mCx, static_cast(flat.get()), flat.Length()); + if (!string) + mOk = JS_FALSE; + + if (!mOk) + return; + + mOk = JS_DefineProperty(mCx, aObject, name, STRING_TO_JSVAL(string), NULL, NULL, JSPROP_ENUMERATE); + } + + void DefineProperty(JSObject *aObject, const char *name, const char *value) + { + nsAutoString string = NS_ConvertASCIItoUTF16(value); + DefineProperty(aObject, name, string); + } + + void ArrayPush(JSObject *aArray, int value) + { + if (!mOk) + return; + + jsval objval = INT_TO_JSVAL(value); + uint32_t length; + mOk = JS_GetArrayLength(mCx, aArray, &length); + + if (!mOk) + return; + + mOk = JS_SetElement(mCx, aArray, length, &objval); + } + + + void ArrayPush(JSObject *aArray, JSObject *aObject) + { + if (!mOk) + return; + + jsval objval = OBJECT_TO_JSVAL(aObject); + uint32_t length; + mOk = JS_GetArrayLength(mCx, aArray, &length); + + if (!mOk) + return; + + mOk = JS_SetElement(mCx, aArray, length, &objval); + } + + JSObject *CreateArray() { + JSObject *array = JS_NewArrayObject(mCx, 0, NULL); + if (!array) + mOk = JS_FALSE; + + return array; + } + + JSObject *CreateObject() { + JSObject *obj = JS_NewObject(mCx, NULL, NULL, NULL); + if (!obj) + mOk = JS_FALSE; + + return obj; + } + + + // We need to ensure that this object lives on the stack so that GC sees it properly + JSObjectBuilder(JSContext *aCx) : mCx(aCx), mOk(JS_TRUE) + { + } + private: + JSObjectBuilder(JSObjectBuilder&); + + JSContext *mCx; + JSObject *mObj; + JSBool mOk; +}; + + diff --git a/tools/profiler/TableTicker.cpp b/tools/profiler/TableTicker.cpp index ded4a7b6ddeb..1c647ac1d3a5 100644 --- a/tools/profiler/TableTicker.cpp +++ b/tools/profiler/TableTicker.cpp @@ -46,6 +46,7 @@ #include "shared-libraries.h" #include "mozilla/StringBuilder.h" #include "mozilla/StackWalk.h" +#include "JSObjectBuilder.h" // we eventually want to make this runtime switchable #if defined(MOZ_PROFILING) && (defined(XP_UNIX) && !defined(XP_MACOSX)) @@ -143,6 +144,7 @@ public: string TagToString(Profile *profile); private: + friend class Profile; union { const char* mTagData; double mTagFloat; @@ -252,6 +254,9 @@ public: void ToString(StringBuilder &profile) { //XXX: this code is not thread safe and needs to be fixed + // can we just have a mutex that guards access to the circular buffer? + // no because the main thread can be stopped while it still has access. + // which will cause a deadlock int oldReadPos = mReadPos; while (mReadPos != mLastFlushPos) { profile.Append(mEntries[mReadPos].TagToString(this).c_str()); @@ -260,6 +265,45 @@ public: mReadPos = oldReadPos; } + JSObject *ToJSObject(JSContext *aCx) + { + JSObjectBuilder b(aCx); + + JSObject *profile = b.CreateObject(); + JSObject *samples = b.CreateArray(); + b.DefineProperty(profile, "samples", samples); + + JSObject *sample = NULL; + JSObject *frames = NULL; + + int oldReadPos = mReadPos; + while (mReadPos != mLastFlushPos) { + ProfileEntry entry = mEntries[mReadPos]; + mReadPos = (mReadPos + 1) % mEntrySize; + switch (entry.mTagName) { + case 's': + sample = b.CreateObject(); + b.DefineProperty(sample, "name", entry.mTagData); + frames = b.CreateArray(); + b.DefineProperty(sample, "frames", frames); + b.ArrayPush(samples, sample); + break; + case 'c': + case 'l': + { + if (sample) { + JSObject *frame = b.CreateObject(); + b.DefineProperty(frame, "location", entry.mTagData); + b.ArrayPush(frames, frame); + } + } + } + } + mReadPos = oldReadPos; + + return profile; + } + void WriteProfile(FILE* stream) { //XXX: this code is not thread safe and needs to be fixed @@ -639,6 +683,17 @@ char* mozilla_sampler_get_profile() return rtn; } +JSObject *mozilla_sampler_get_profile_data(JSContext *aCx) +{ + TableTicker *t = mozilla::tls::get(pkey_ticker); + if (!t) { + return NULL; + } + + return t->GetProfile()->ToJSObject(aCx); +} + + const char** mozilla_sampler_get_features() { static const char* features[] = { diff --git a/tools/profiler/nsIProfiler.idl b/tools/profiler/nsIProfiler.idl index 0d3942c0da21..718e1b0f25a6 100644 --- a/tools/profiler/nsIProfiler.idl +++ b/tools/profiler/nsIProfiler.idl @@ -43,6 +43,8 @@ interface nsIProfiler : nsISupports in PRUint32 aFeatureCount); void StopProfiler(); string GetProfile(); + [implicit_jscontext] + jsval getProfileData(); boolean IsActive(); void GetResponsivenessTimes(out PRUint32 aCount, [retval, array, size_is(aCount)] out double aResult); void GetFeatures(out PRUint32 aCount, [retval, array, size_is(aCount)] out string aFeatures); diff --git a/tools/profiler/nsProfiler.cpp b/tools/profiler/nsProfiler.cpp index ea496f3daa73..01c68e52fb5a 100644 --- a/tools/profiler/nsProfiler.cpp +++ b/tools/profiler/nsProfiler.cpp @@ -44,6 +44,7 @@ #include "nsMemory.h" #include "shared-libraries.h" #include "nsString.h" +#include "jsapi.h" using std::string; @@ -125,6 +126,18 @@ nsProfiler::GetSharedLibraryInformation(nsAString& aOutString) return NS_OK; } + + +NS_IMETHODIMP nsProfiler::GetProfileData(JSContext* aCx, jsval* aResult) +{ + JSObject *obj = SAMPLER_GET_PROFILE_DATA(aCx); + if (!obj) + return NS_ERROR_FAILURE; + + *aResult = OBJECT_TO_JSVAL(obj); + return NS_OK; +} + NS_IMETHODIMP nsProfiler::IsActive(bool *aIsActive) { diff --git a/tools/profiler/sampler.h b/tools/profiler/sampler.h index 1dabe3dfd3ad..29330127c349 100644 --- a/tools/profiler/sampler.h +++ b/tools/profiler/sampler.h @@ -99,6 +99,7 @@ #define SAMPLER_SAVE() // Returned string must be free'ed #define SAMPLER_GET_PROFILE() NULL +#define SAMPLER_GET_PROFILE_DATA(ctx) NULL #define SAMPLER_RESPONSIVENESS(time) NULL #define SAMPLER_GET_RESPONSIVENESS() NULL #define SAMPLER_GET_FEATURES() NULL diff --git a/tools/profiler/sps_sampler.h b/tools/profiler/sps_sampler.h index f5f35281948d..16c956ba478e 100644 --- a/tools/profiler/sps_sampler.h +++ b/tools/profiler/sps_sampler.h @@ -40,6 +40,7 @@ #include #include "thread_helper.h" #include "nscore.h" +#include "jsapi.h" #include "mozilla/TimeStamp.h" using mozilla::TimeStamp; @@ -68,6 +69,7 @@ extern bool stack_key_initialized; #define SAMPLER_GET_RESPONSIVENESS() mozilla_sampler_get_responsiveness() #define SAMPLER_SAVE() mozilla_sampler_save() #define SAMPLER_GET_PROFILE() mozilla_sampler_get_profile() +#define SAMPLER_GET_PROFILE_DATA(ctx) mozilla_sampler_get_profile_data(ctx) #define SAMPLER_GET_FEATURES() mozilla_sampler_get_features() // 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 @@ -140,6 +142,7 @@ void mozilla_sampler_responsiveness(TimeStamp time); const double* mozilla_sampler_get_responsiveness(); void mozilla_sampler_save(); char* mozilla_sampler_get_profile(); +JSObject *mozilla_sampler_get_profile_data(JSContext *aCx); const char** mozilla_sampler_get_features(); void mozilla_sampler_init();