Bug 799640 - Part 2: Save profiles on shutdown using custom JSON encoder. r=ehsan
☠☠ backed out by fd22236ffb15 ☠ ☠
authorBenoit Girard <b56girard@gmail.com>
Fri, 30 Nov 2012 12:49:20 -0500
changeset 114614 b130bb991d84db729555ad5b837dc3c5eb1a74a7
parent 114613 e47b059493cfb024e03b446701ede2e677d0f8bf
child 114615 133c704dbcc68e7f573d3664f4e2107e2e4f2090
push idunknown
push userunknown
push dateunknown
reviewersehsan
bugs799640
milestone20.0a1
Bug 799640 - Part 2: Save profiles on shutdown using custom JSON encoder. r=ehsan
toolkit/xre/nsAppRunner.cpp
tools/profiler/JSAObjectBuilder.h
tools/profiler/JSCustomObjectBuilder.cpp
tools/profiler/JSCustomObjectBuilder.h
tools/profiler/JSObjectBuilder.cpp
tools/profiler/JSObjectBuilder.h
tools/profiler/Makefile.in
tools/profiler/TableTicker.cpp
tools/profiler/sps_sampler.h
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -3932,16 +3932,17 @@ XREMain::XRE_main(int argc, char* argv[]
     SaveFileToEnvIfUnset("XRE_PROFILE_PATH", mProfD);
     SaveFileToEnvIfUnset("XRE_PROFILE_LOCAL_PATH", mProfLD);
     SaveWordToEnvIfUnset("XRE_PROFILE_NAME", mProfileName);
 
 #ifdef MOZ_WIDGET_GTK
     MOZ_gdk_display_close(mGdkDisplay);
 #endif
 
+    SAMPLER_SHUTDOWN();
     rv = LaunchChild(mNativeApp, true);
 
 #ifdef MOZ_CRASHREPORTER
     if (mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER)
       CrashReporter::UnsetExceptionHandler();
 #endif
     return rv == NS_ERROR_LAUNCHED_CHILD_PROCESS ? 0 : 1;
   }
@@ -3954,16 +3955,18 @@ XREMain::XRE_main(int argc, char* argv[]
 
 #ifdef MOZ_CRASHREPORTER
   if (mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER)
       CrashReporter::UnsetExceptionHandler();
 #endif
 
   XRE_DeinitCommandLine();
 
+  SAMPLER_SHUTDOWN();
+
   return NS_FAILED(rv) ? 1 : 0;
 }
 
 #if defined(MOZ_METRO) && defined(XP_WIN)
 extern bool XRE_MetroCoreApplicationRun();
 static XREMain* xreMainPtr;
 
 // must be called by the thread we want as the main thread
@@ -4065,16 +4068,17 @@ XRE_mainMetro(int argc, char* argv[], co
   if (!XRE_MetroCoreApplicationRun()) {
     return 1;
   }
 
   // XRE_metroShutdown should have already been called on the worker
   // thread that called XRE_metroStartup.
   NS_ASSERTION(!xreMainPtr->mScopedXPCom,
                "XPCOM Shutdown hasn't occured, and we are exiting.");
+  SAMPLER_SHUTDOWN();
   return 0;
 }
 
 void SetWindowsEnvironment(WindowsEnvironmentType aEnvID);
 #endif // MOZ_METRO || !defined(XP_WIN)
 
 int
 XRE_main(int argc, char* argv[], const nsXREAppData* aAppData, uint32_t aFlags)
new file mode 100644
--- /dev/null
+++ b/tools/profiler/JSAObjectBuilder.h
@@ -0,0 +1,30 @@
+/* -*- 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 JSAOBJECTBUILDER_H
+#define JSAOBJECTBUILDER_H
+
+class JSCustomObject;
+class JSCustomArray;
+class nsAString;
+
+class JSAObjectBuilder
+{
+public:
+  virtual ~JSAObjectBuilder() = 0;
+
+  virtual void DefineProperty(JSCustomObject *aObject, const char *name, JSCustomObject *aValue) = 0;
+  virtual void DefineProperty(JSCustomObject *aObject, const char *name, JSCustomArray *aValue) = 0;
+  virtual void DefineProperty(JSCustomObject *aObject, const char *name, int value) = 0;
+  virtual void DefineProperty(JSCustomObject *aObject, const char *name, double value) = 0;
+  virtual void DefineProperty(JSCustomObject *aObject, const char *name, const char *value) = 0;
+  virtual void ArrayPush(JSCustomArray *aArray, int value) = 0;
+  virtual void ArrayPush(JSCustomArray *aArray, const char *value) = 0;
+  virtual void ArrayPush(JSCustomArray *aArray, JSCustomObject *aObject) = 0;
+  virtual JSCustomArray  *CreateArray() = 0;
+  virtual JSCustomObject *CreateObject() = 0;
+};
+
+#endif
new file mode 100644
--- /dev/null
+++ b/tools/profiler/JSCustomObjectBuilder.cpp
@@ -0,0 +1,311 @@
+/* -*- 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 "JSCustomObjectBuilder.h"
+#include "nsStringGlue.h"
+#include "nsDataHashtable.h"
+#include "nsUTF8Utils.h"
+
+#if _MSC_VER
+ #define snprintf _snprintf
+#endif
+
+// These are owned and deleted by JSCustomObject
+struct PropertyValue {
+  virtual ~PropertyValue() {}
+  virtual void SendToStream(std::ostream& stream) = 0;
+};
+
+template <typename T>
+struct finalizer_impl
+{
+  static void run(T) {}
+};
+
+template <typename T>
+struct finalizer_impl<T*>
+{
+  static void run(T* p) {
+    delete p;
+  }
+};
+
+template <>
+struct finalizer_impl<char *>
+{
+  static void run(char* p) {
+    free(p);
+  }
+};
+
+template <class T>
+class TemplatePropertyValue : public PropertyValue {
+public:
+  TemplatePropertyValue(T aValue)
+    : mValue(aValue)
+  {}
+
+  ~TemplatePropertyValue() {
+    finalizer_impl<T>::run(mValue);
+  }
+
+  virtual void SendToStream(std::ostream& stream);
+private:
+  T mValue;
+};
+
+// Escape a UTF8 string to a stream. When an illegal encoding
+// is found it will insert "INVALID" and the function will return.
+void EscapeToStream(std::ostream& stream, const char* str) {
+  stream << "\"";
+
+  size_t len = strlen(str);
+  const char* end = &str[len];
+  while (str < end) {
+    bool err;
+    const char* utf8CharStart = str;
+    uint32_t ucs4Char = UTF8CharEnumerator::NextChar(&str, end, &err);
+
+    if (err) {
+      // Encoding error
+      stream << "INVALID\"";
+      return;
+    }
+
+    // See http://www.ietf.org/rfc/rfc4627.txt?number=4627
+    // characters that must be escaped: quotation mark,
+    // reverse solidus, and the control characters
+    // (U+0000 through U+001F).
+    if (ucs4Char == '\"') {
+      stream << "\\\"";
+    } else if (ucs4Char == '\\') {
+      stream << "\\\\";
+    } else if (ucs4Char > 0xFF) {
+      PRUnichar chr[2];
+      ConvertUTF8toUTF16 encoder(chr);
+      encoder.write(utf8CharStart, str-utf8CharStart);
+      char escChar[13];
+      snprintf(escChar, mozilla::ArrayLength(escChar), "\\u%04X\\u%04X", chr[0], chr[1]);
+      stream << escChar;
+    } else if (ucs4Char < 0x1F || ucs4Char > 0xFF) {
+      char escChar[7];
+      snprintf(escChar, mozilla::ArrayLength(escChar), "\\u%04X", ucs4Char);
+      stream << escChar;
+    } else {
+      stream << char(ucs4Char);
+    }
+  }
+  stream << "\"";
+}
+
+class JSCustomObject {
+public:
+  JSCustomObject() {
+    mProperties.Init();
+  }
+  ~JSCustomObject();
+
+  friend std::ostream& operator<<(std::ostream& stream, JSCustomObject* entry);
+
+  template<class T>
+  void AddProperty(const char* aName, T aValue) {
+    mProperties.Put(nsDependentCString(aName), new TemplatePropertyValue<T>(aValue));
+  }
+
+  nsDataHashtable<nsCStringHashKey, PropertyValue*> mProperties;
+};
+
+class JSCustomArray {
+public:
+  nsTArray<PropertyValue*> mValues;
+
+  friend std::ostream& operator<<(std::ostream& stream, JSCustomArray* entry);
+
+  template<class T>
+  void AppendElement(T aValue) {
+    mValues.AppendElement(new TemplatePropertyValue<T>(aValue));
+  }
+};
+
+template <typename T>
+struct SendToStreamImpl
+{
+  static void run(std::ostream& stream, const T& t) {
+    stream << t;
+  }
+};
+
+template<typename T>
+struct SendToStreamImpl<T*>
+{
+  static void run(std::ostream& stream, T* t) {
+    stream << *t;
+  }
+};
+
+template <>
+struct SendToStreamImpl<char *>
+{
+  static void run(std::ostream& stream, char* p) {
+    EscapeToStream(stream, p);
+  }
+};
+
+template <>
+struct SendToStreamImpl<JSCustomObject*>
+{
+  static void run(std::ostream& stream, JSCustomObject* p) {
+    stream << p;
+  }
+};
+
+template <>
+struct SendToStreamImpl<JSCustomArray*>
+{
+  static void run(std::ostream& stream, JSCustomArray* p) {
+    stream << p;
+  }
+};
+
+template <class T> void
+TemplatePropertyValue<T>::SendToStream(std::ostream& stream)
+{
+  SendToStreamImpl<T>::run(stream, mValue);
+}
+
+struct JSONStreamClosure {
+  std::ostream& mStream;
+  bool mNeedsComma;
+};
+
+PLDHashOperator HashTableOutput(const nsACString& aKey, PropertyValue* aValue, void* stream)
+{
+  JSONStreamClosure& streamClosure = *(JSONStreamClosure*)stream;
+  if (streamClosure.mNeedsComma) {
+    streamClosure.mStream << ",";
+  }
+  streamClosure.mNeedsComma = true;
+  EscapeToStream(streamClosure.mStream, (const char*)aKey.BeginReading());
+  streamClosure.mStream << ":";
+  aValue->SendToStream(streamClosure.mStream);
+  return PLDHashOperator::PL_DHASH_NEXT;
+}
+
+std::ostream&
+operator<<(std::ostream& stream, JSCustomObject* entry)
+{
+  JSONStreamClosure streamClosure = {stream, false};
+  stream << "{";
+  entry->mProperties.EnumerateRead(HashTableOutput, &streamClosure);
+  stream << "}";
+  return stream;
+}
+
+std::ostream&
+operator<<(std::ostream& stream, JSCustomArray* entry)
+{
+  bool needsComma = false;
+  stream << "[";
+  for (int i = 0; i < entry->mValues.Length(); i++) {
+    if (needsComma) {
+      stream << ",";
+    }
+    entry->mValues[i]->SendToStream(stream);
+    needsComma = true;
+  }
+  stream << "]";
+  return stream;
+}
+
+PLDHashOperator HashTableFree(const nsACString& aKey, PropertyValue* aValue, void* stream)
+{
+  delete aValue;
+  return PLDHashOperator::PL_DHASH_NEXT;
+}
+
+JSCustomObject::~JSCustomObject()
+{
+  mProperties.EnumerateRead(HashTableFree, nullptr);
+}
+
+JSAObjectBuilder::~JSAObjectBuilder()
+{
+}
+
+JSCustomObjectBuilder::JSCustomObjectBuilder()
+{}
+
+void
+JSCustomObjectBuilder::DeleteObject(JSCustomObject* aObject)
+{
+  delete aObject;
+}
+
+void
+JSCustomObjectBuilder::Serialize(JSCustomObject* aObject, std::ostream& stream)
+{
+  stream << aObject;
+}
+
+void
+JSCustomObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, JSCustomObject *aValue)
+{
+  aObject->AddProperty(name, aValue);
+}
+
+void
+JSCustomObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, JSCustomArray *aValue)
+{
+  aObject->AddProperty(name, aValue);
+}
+
+void
+JSCustomObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, int aValue)
+{
+  aObject->AddProperty(name, aValue);
+}
+
+void
+JSCustomObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, double aValue)
+{
+  aObject->AddProperty(name, aValue);
+}
+
+void
+JSCustomObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, const char *aValue)
+{
+  // aValue copy will be freed by the property desctructor (template specialization)
+  aObject->AddProperty(name, strdup(aValue));
+}
+
+void
+JSCustomObjectBuilder::ArrayPush(JSCustomArray *aArray, int aValue)
+{
+  aArray->AppendElement(aValue);
+}
+
+void
+JSCustomObjectBuilder::ArrayPush(JSCustomArray *aArray, const char *aValue)
+{
+  // aValue copy will be freed by the property desctructor (template specialization)
+  aArray->AppendElement(strdup(aValue));
+}
+
+void
+JSCustomObjectBuilder::ArrayPush(JSCustomArray *aArray, JSCustomObject *aObject)
+{
+  aArray->AppendElement(aObject);
+}
+
+JSCustomArray*
+JSCustomObjectBuilder::CreateArray() {
+  return new JSCustomArray();
+}
+
+JSCustomObject*
+JSCustomObjectBuilder::CreateObject() {
+  return new JSCustomObject();
+}
+
new file mode 100644
--- /dev/null
+++ b/tools/profiler/JSCustomObjectBuilder.h
@@ -0,0 +1,57 @@
+/* -*- 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 JSCUSTOMOBJECTBUILDER_H
+#define JSCUSTOMOBJECTBUILDER_H
+
+#include <ostream>
+#include "JSAObjectBuilder.h"
+
+class JSCustomObject;
+class JSCustomArray;
+class JSCustomObjectBuilder;
+
+class JSCustomObjectBuilder : public JSAObjectBuilder
+{
+public:
+
+  // We need to ensure that this object lives on the stack so that GC sees it properly
+  JSCustomObjectBuilder();
+
+  void Serialize(JSCustomObject* aObject, std::ostream& stream);
+
+  void DefineProperty(JSCustomObject *aObject, const char *name, JSCustomObject *aValue);
+  void DefineProperty(JSCustomObject *aObject, const char *name, JSCustomArray *aValue);
+  void DefineProperty(JSCustomObject *aObject, const char *name, int value);
+  void DefineProperty(JSCustomObject *aObject, const char *name, double value);
+  void DefineProperty(JSCustomObject *aObject, const char *name, const char *value, size_t valueLength);
+  void DefineProperty(JSCustomObject *aObject, const char *name, const char *value);
+  void ArrayPush(JSCustomArray *aArray, int value);
+  void ArrayPush(JSCustomArray *aArray, const char *value);
+  void ArrayPush(JSCustomArray *aArray, JSCustomObject *aObject);
+  JSCustomArray  *CreateArray();
+  JSCustomObject *CreateObject();
+
+  // Delete this object and all of its descendant
+  void DeleteObject(JSCustomObject* aObject);
+
+private:
+  // This class can't be copied
+  JSCustomObjectBuilder(const JSCustomObjectBuilder&);
+  JSCustomObjectBuilder& operator=(const JSCustomObjectBuilder&);
+
+  void* operator new(size_t);
+  void* operator new[](size_t);
+  void operator delete(void*) {
+    // Since JSCustomObjectBuilder has a virtual destructor the compiler
+    // has to provide a destructor in the object file that will call
+    // operate delete in case there is a derived class since its
+    // destructor wont know how to free this instance.
+    abort();
+  }
+  void operator delete[](void*);
+};
+
+#endif
--- a/tools/profiler/JSObjectBuilder.cpp
+++ b/tools/profiler/JSObjectBuilder.cpp
@@ -6,144 +6,148 @@
 #include "jsapi.h"
 #include "nsStringGlue.h"
 #include "JSObjectBuilder.h"
 
 JSObjectBuilder::JSObjectBuilder(JSContext *aCx) : mCx(aCx), mOk(JS_TRUE)
 {}
 
 void
-JSObjectBuilder::DefineProperty(JSObject *aObject, const char *name, JSObject *aValue)
+JSObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, JSCustomArray *aValue)
 {
-  if (!mOk)
-    return;
-
-  mOk = JS_DefineProperty(mCx, aObject, name, OBJECT_TO_JSVAL(aValue), NULL, NULL, JSPROP_ENUMERATE);
+  DefineProperty(aObject, name, (JSCustomObject*)aValue);
 }
 
 void
-JSObjectBuilder::DefineProperty(JSObject *aObject, const char *name, int value)
+JSObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, JSCustomObject *aValue)
 {
   if (!mOk)
     return;
 
-  mOk = JS_DefineProperty(mCx, aObject, name, INT_TO_JSVAL(value), NULL, NULL, JSPROP_ENUMERATE);
+  mOk = JS_DefineProperty(mCx, (JSObject*)aObject, name, OBJECT_TO_JSVAL((JSObject*)aValue), nullptr, nullptr, JSPROP_ENUMERATE);
 }
 
 void
-JSObjectBuilder::DefineProperty(JSObject *aObject, const char *name, double value)
+JSObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, int value)
 {
   if (!mOk)
     return;
 
-  mOk = JS_DefineProperty(mCx, aObject, name, DOUBLE_TO_JSVAL(value), NULL, NULL, JSPROP_ENUMERATE);
+  mOk = JS_DefineProperty(mCx, (JSObject*)aObject, name, INT_TO_JSVAL(value), nullptr, nullptr, JSPROP_ENUMERATE);
 }
 
 void
-JSObjectBuilder::DefineProperty(JSObject *aObject, const char *name, nsAString &value)
+JSObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, double value)
+{
+  if (!mOk)
+    return;
+
+  mOk = JS_DefineProperty(mCx, (JSObject*)aObject, name, DOUBLE_TO_JSVAL(value), nullptr, nullptr, JSPROP_ENUMERATE);
+}
+
+void
+JSObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, nsAString &value)
 {
   if (!mOk)
     return;
 
   const nsString &flat = PromiseFlatString(value);
   JSString *string = JS_NewUCStringCopyN(mCx, static_cast<const jschar*>(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);
+  mOk = JS_DefineProperty(mCx, (JSObject*)aObject, name, STRING_TO_JSVAL(string), nullptr, nullptr, JSPROP_ENUMERATE);
 }
 
 void
-JSObjectBuilder::DefineProperty(JSObject *aObject, const char *name, const char *value, size_t valueLength)
+JSObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, const char *value, size_t valueLength)
 {
   if (!mOk)
     return;
 
   JSString *string = JS_InternStringN(mCx, value, valueLength);
   if (!string) {
     mOk = JS_FALSE;
     return;
   }
 
-  mOk = JS_DefineProperty(mCx, aObject, name, STRING_TO_JSVAL(string), NULL, NULL, JSPROP_ENUMERATE);
-}
+  mOk = JS_DefineProperty(mCx, (JSObject*)aObject, name, STRING_TO_JSVAL(string), nullptr, nullptr, JSPROP_ENUMERATE); }
 
 void
-JSObjectBuilder::DefineProperty(JSObject *aObject, const char *name, const char *value)
+JSObjectBuilder::DefineProperty(JSCustomObject *aObject, const char *name, const char *value)
 {
   DefineProperty(aObject, name, value, strlen(value));
 }
 
 void
-JSObjectBuilder::ArrayPush(JSObject *aArray, int value)
+JSObjectBuilder::ArrayPush(JSCustomArray *aArray, int value)
 {
   if (!mOk)
     return;
 
   jsval objval = INT_TO_JSVAL(value);
   uint32_t length;
-  mOk = JS_GetArrayLength(mCx, aArray, &length);
+  mOk = JS_GetArrayLength(mCx, (JSObject*)aArray, &length);
 
   if (!mOk)
     return;
 
-  mOk = JS_SetElement(mCx, aArray, length, &objval);
+  mOk = JS_SetElement(mCx, (JSObject*)aArray, length, &objval);
 }
 
 void
-JSObjectBuilder::ArrayPush(JSObject *aArray, const char *value)
+JSObjectBuilder::ArrayPush(JSCustomArray *aArray, const char *value)
 {
   if (!mOk)
     return;
 
   JSString *string = JS_NewStringCopyN(mCx, value, strlen(value));
   if (!string) {
     mOk = JS_FALSE;
     return;
   }
 
   jsval objval = STRING_TO_JSVAL(string);
   uint32_t length;
-  mOk = JS_GetArrayLength(mCx, aArray, &length);
+  mOk = JS_GetArrayLength(mCx, (JSObject*)aArray, &length);
 
   if (!mOk)
     return;
 
-  mOk = JS_SetElement(mCx, aArray, length, &objval);
+  mOk = JS_SetElement(mCx, (JSObject*)aArray, length, &objval);
 }
 
 void
-JSObjectBuilder::ArrayPush(JSObject *aArray, JSObject *aObject)
+JSObjectBuilder::ArrayPush(JSCustomArray *aArray, JSCustomObject *aObject)
 {
   if (!mOk)
     return;
 
-  jsval objval = OBJECT_TO_JSVAL(aObject);
-  uint32_t length;
-  mOk = JS_GetArrayLength(mCx, aArray, &length);
+  jsval objval = OBJECT_TO_JSVAL((JSObject*)aObject); uint32_t length;
+  mOk = JS_GetArrayLength(mCx, (JSObject*)aArray, &length);
 
   if (!mOk)
     return;
 
-  mOk = JS_SetElement(mCx, aArray, length, &objval);
+  mOk = JS_SetElement(mCx, (JSObject*)aArray, length, &objval);
 }
 
-JSObject*
+JSCustomArray*
 JSObjectBuilder::CreateArray() {
-  JSObject *array = JS_NewArrayObject(mCx, 0, NULL);
+  JSCustomArray *array = (JSCustomArray*)JS_NewArrayObject(mCx, 0, nullptr);
   if (!array)
     mOk = JS_FALSE;
 
   return array;
 }
 
-JSObject*
+JSCustomObject*
 JSObjectBuilder::CreateObject() {
-  JSObject *obj = JS_NewObject(mCx, NULL, NULL, NULL);
+  JSCustomObject *obj = (JSCustomObject*)JS_NewObject(mCx, nullptr, nullptr, nullptr);
   if (!obj)
     mOk = JS_FALSE;
 
   return obj;
 }
 
--- a/tools/profiler/JSObjectBuilder.h
+++ b/tools/profiler/JSObjectBuilder.h
@@ -1,39 +1,62 @@
 /* -*- 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/. */
 
-class JSObject;
-class JSObjectBuilder;
+#ifndef JSOBJECTBUILDER_H
+#define JSOBJECTBUILDER_H
+
+#include "JSAObjectBuilder.h"
+
+class JSCustomObject;
+class JSCustomObjectBuilder;
+class JSContext;
 class nsAString;
 
 /* 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
+class JSObjectBuilder : public JSAObjectBuilder
 {
 public:
-
   // We need to ensure that this object lives on the stack so that GC sees it properly
-  JSObjectBuilder(JSContext *aCx);
+  explicit JSObjectBuilder(JSContext *aCx);
+  ~JSObjectBuilder() {}
 
-  void DefineProperty(JSObject *aObject, const char *name, JSObject *aValue);
-  void DefineProperty(JSObject *aObject, const char *name, int value);
-  void DefineProperty(JSObject *aObject, const char *name, double value);
-  void DefineProperty(JSObject *aObject, const char *name, nsAString &value);
-  void DefineProperty(JSObject *aObject, const char *name, const char *value, size_t valueLength);
-  void DefineProperty(JSObject *aObject, const char *name, const char *value);
-  void ArrayPush(JSObject *aArray, int value);
-  void ArrayPush(JSObject *aArray, const char *value);
-  void ArrayPush(JSObject *aArray, JSObject *aObject);
-  JSObject *CreateArray();
-  JSObject *CreateObject();
+  void DefineProperty(JSCustomObject *aObject, const char *name, JSCustomObject *aValue);
+  void DefineProperty(JSCustomObject *aObject, const char *name, JSCustomArray *aValue);
+  void DefineProperty(JSCustomObject *aObject, const char *name, int value);
+  void DefineProperty(JSCustomObject *aObject, const char *name, double value);
+  void DefineProperty(JSCustomObject *aObject, const char *name, nsAString &value);
+  void DefineProperty(JSCustomObject *aObject, const char *name, const char *value, size_t valueLength);
+  void DefineProperty(JSCustomObject *aObject, const char *name, const char *value);
+  void ArrayPush(JSCustomArray *aArray, int value);
+  void ArrayPush(JSCustomArray *aArray, const char *value);
+  void ArrayPush(JSCustomArray *aArray, JSCustomArray *aObject);
+  void ArrayPush(JSCustomArray *aArray, JSCustomObject *aObject);
+  JSCustomArray *CreateArray();
+  JSCustomObject *CreateObject();
+
+  JSObject* GetJSObject(JSCustomObject* aObject) { return (JSObject*)aObject; }
 
 private:
-  JSObjectBuilder(JSObjectBuilder&);
+  JSObjectBuilder(const JSObjectBuilder&);
+  JSObjectBuilder& operator=(const JSObjectBuilder&);
+
+  void* operator new(size_t);
+  void* operator new[](size_t);
+  void operator delete(void*) {
+    // Since JSObjectBuilder has a virtual destructor the compiler
+    // has to provide a destructor in the object file that will call
+    // operate delete in case there is a derived class since its
+    // destructor wont know how to free this instance.
+    abort();
+  }
+  void operator delete[](void*);
 
   JSContext *mCx;
   JSObject *mObj;
   int mOk;
 };
 
+#endif
 
--- a/tools/profiler/Makefile.in
+++ b/tools/profiler/Makefile.in
@@ -59,16 +59,17 @@ EXPORT_LIBRARY  = 1
 LIBXUL_LIBRARY  = 1
 IS_COMPONENT    = 1
 
 CPPSRCS		= \
   nsProfilerFactory.cpp \
   nsProfiler.cpp \
   TableTicker.cpp \
   JSObjectBuilder.cpp \
+  JSCustomObjectBuilder.cpp \
   $(NULL)
 
 XPIDLSRCS = \
   nsIProfiler.idl \
   $(NULL)
 
 EXTRA_JS_MODULES = \
   Profiler.jsm \
--- a/tools/profiler/TableTicker.cpp
+++ b/tools/profiler/TableTicker.cpp
@@ -1,28 +1,28 @@
 /* -*- 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 <string>
 #include <stdio.h>
-#include <iostream>
 #include <fstream>
 #include <sstream>
 #include "sps_sampler.h"
 #include "platform.h"
 #include "nsXULAppAPI.h"
 #include "nsThreadUtils.h"
 #include "prenv.h"
 #include "shared-libraries.h"
 #include "mozilla/StackWalk.h"
 
 // JSON
 #include "JSObjectBuilder.h"
+#include "JSCustomObjectBuilder.h"
 #include "nsIJSRuntimeService.h"
 
 // Meta
 #include "nsXPCOM.h"
 #include "nsXPCOMCID.h"
 #include "nsIHttpProtocolHandler.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIXULRuntime.h"
@@ -302,27 +302,41 @@ public:
       }
 
       aCallback(entry, tagStringData);
 
       readPos = (readPos + incBy) % mEntrySize;
     }
   }
 
-  JSObject *ToJSObject(JSContext *aCx)
+  void ToStreamAsJSON(std::ostream& stream)
+  {
+    JSCustomObjectBuilder b;
+    JSCustomObject *profile = b.CreateObject();
+    BuildJSObject(b, profile);
+    b.Serialize(profile, stream);
+    b.DeleteObject(profile);
+  }
+
+  JSCustomObject *ToJSObject(JSContext *aCx)
   {
     JSObjectBuilder b(aCx);
+    JSCustomObject *profile = b.CreateObject();
+    BuildJSObject(b, profile);
 
-    JSObject *profile = b.CreateObject();
-    JSObject *samples = b.CreateArray();
+    return profile;
+  }
+
+  void BuildJSObject(JSAObjectBuilder& b, JSCustomObject* profile) {
+    JSCustomArray *samples = b.CreateArray();
     b.DefineProperty(profile, "samples", samples);
 
-    JSObject *sample = NULL;
-    JSObject *frames = NULL;
-    JSObject *marker = NULL;
+    JSCustomObject *sample = nullptr;
+    JSCustomArray *frames = nullptr;
+    JSCustomArray *marker = nullptr;
 
     int readPos = mReadPos;
     while (readPos != mLastFlushPos) {
       // Number of tag consumed
       int incBy = 1;
       ProfileEntry entry = mEntries[readPos];
 
       // Read ahead to the next tag, if it's a 'd' tag process it now
@@ -377,17 +391,17 @@ public:
               b.DefineProperty(sample, "time", entry.mTagFloat);
             }
           }
           break;
         case 'c':
         case 'l':
           {
             if (sample) {
-              JSObject *frame = b.CreateObject();
+              JSCustomObject *frame = b.CreateObject();
               if (entry.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)entry.mTagPtr;
                 snprintf(tagBuff, DYNAMIC_MAX_STRING, "%#llx", pc);
                 b.DefineProperty(frame, "location", tagBuff);
               } else {
@@ -401,18 +415,16 @@ public:
                 }
               }
               b.ArrayPush(frames, frame);
             }
           }
       }
       readPos = (readPos + incBy) % mEntrySize;
     }
-
-    return profile;
   }
 
   ProfileStack* GetStack()
   {
     return mStack;
   }
 private:
   // Circular buffer 'Keep One Slot Open' implementation
@@ -468,25 +480,27 @@ class TableTicker: public Sampler {
 
   virtual void HandleSaveRequest();
 
   ThreadProfile* GetPrimaryThreadProfile()
   {
     return &mPrimaryThreadProfile;
   }
 
+  void ToStreamAsJSON(std::ostream& stream);
   JSObject *ToJSObject(JSContext *aCx);
-  JSObject *GetMetaJSObject(JSObjectBuilder& b);
+  JSCustomObject *GetMetaJSCustomObject(JSAObjectBuilder& b);
 
   const bool ProfileJS() { return mProfileJS; }
 
 private:
   // Not implemented on platforms which do not support backtracing
   void doBacktrace(ThreadProfile &aProfile, TickSample* aSample);
 
+  void BuildJSObject(JSAObjectBuilder& b, JSCustomObject* profile);
 private:
   // This represent the application's main thread (SAMPLER_INIT)
   ThreadProfile mPrimaryThreadProfile;
   TimeStamp mStartTime;
   bool mSaveRequested;
   bool mUseStackWalk;
   bool mJankOnly;
   bool mProfileJS;
@@ -508,17 +522,16 @@ WriteCallback(const jschar *buf, uint32_
  * to be sure that it is not being modified while saving.
  */
 class SaveProfileTask : public nsRunnable {
 public:
   SaveProfileTask() {}
 
   NS_IMETHOD Run() {
     TableTicker *t = tlsTicker.get();
-
     // Pause the profiler during saving.
     // This will prevent us from recording sampling
     // regarding profile saving. This will also
     // prevent bugs caused by the circular buffer not
     // being thread safe. Bug 750989.
     t->SetPaused(true);
 
     // Get file path
@@ -538,17 +551,17 @@ public:
     if (NS_FAILED(rv))
       return rv;
 
     rv = tmpFile->GetNativePath(tmpPath);
     if (NS_FAILED(rv))
       return rv;
 #endif
 
-    // Create a JSContext to run a JSObjectBuilder :(
+    // Create a JSContext to run a JSCustomObjectBuilder :(
     // Based on XPCShellEnvironment
     JSRuntime *rt;
     JSContext *cx;
     nsCOMPtr<nsIJSRuntimeService> rtsvc = do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
     if (!rtsvc || NS_FAILED(rtsvc->GetRuntime(&rt)) || !rt) {
       LOG("failed to get RuntimeService");
       return NS_ERROR_FAILURE;;
     }
@@ -570,52 +583,49 @@ public:
 
       std::ofstream stream;
       stream.open(tmpPath.get());
       // Pause the profiler during saving.
       // This will prevent us from recording sampling
       // regarding profile saving. This will also
       // prevent bugs caused by the circular buffer not
       // being thread safe. Bug 750989.
-      t->SetPaused(true);
       if (stream.is_open()) {
         JSAutoCompartment autoComp(cx, obj);
         JSObject* profileObj = mozilla_sampler_get_profile_data(cx);
         jsval val = OBJECT_TO_JSVAL(profileObj);
         JS_Stringify(cx, &val, nullptr, JSVAL_NULL, WriteCallback, &stream);
         stream.close();
         LOGF("Saved to %s", tmpPath.get());
       } else {
         LOG("Fail to open profile log file.");
       }
     }
     JS_EndRequest(cx);
     JS_DestroyContext(cx);
 
-    t->SetPaused(false);
-
     return NS_OK;
   }
 };
 
 void TableTicker::HandleSaveRequest()
 {
   if (!mSaveRequested)
     return;
   mSaveRequested = false;
 
   // TODO: Use use the ipc/chromium Tasks here to support processes
   // without XPCOM.
   nsCOMPtr<nsIRunnable> runnable = new SaveProfileTask();
   NS_DispatchToMainThread(runnable);
 }
 
-JSObject* TableTicker::GetMetaJSObject(JSObjectBuilder& b)
+JSCustomObject* TableTicker::GetMetaJSCustomObject(JSAObjectBuilder& b)
 {
-  JSObject *meta = b.CreateObject();
+  JSCustomObject *meta = b.CreateObject();
 
   b.DefineProperty(meta, "version", 2);
   b.DefineProperty(meta, "interval", interval());
   b.DefineProperty(meta, "stackwalk", mUseStackWalk);
   b.DefineProperty(meta, "jank", mJankOnly);
   b.DefineProperty(meta, "processType", XRE_GetProcessType());
 
   nsresult res;
@@ -656,40 +666,54 @@ JSObject* TableTicker::GetMetaJSObject(J
     res = appInfo->GetName(string);
     if (!NS_FAILED(res))
       b.DefineProperty(meta, "product", string.Data());
   }
 
   return meta;
 }
 
+void TableTicker::ToStreamAsJSON(std::ostream& stream)
+{
+  JSCustomObjectBuilder b;
+  JSCustomObject* profile = b.CreateObject();
+  BuildJSObject(b, profile);
+  b.Serialize(profile, stream);
+  b.DeleteObject(profile);
+}
+
 JSObject* TableTicker::ToJSObject(JSContext *aCx)
 {
   JSObjectBuilder b(aCx);
+  JSCustomObject* profile = b.CreateObject();
+  BuildJSObject(b, profile);
+  JSObject* jsProfile = b.GetJSObject(profile);
 
-  JSObject *profile = b.CreateObject();
+  return jsProfile;
+}
 
+void TableTicker::BuildJSObject(JSAObjectBuilder& b, JSCustomObject* profile)
+{
   // Put shared library info
   b.DefineProperty(profile, "libs", GetSharedLibraryInfoString().c_str());
 
   // Put meta data
-  JSObject *meta = GetMetaJSObject(b);
+  JSCustomObject *meta = GetMetaJSCustomObject(b);
   b.DefineProperty(profile, "meta", meta);
 
   // Lists the samples for each ThreadProfile
-  JSObject *threads = b.CreateArray();
+  JSCustomArray *threads = b.CreateArray();
   b.DefineProperty(profile, "threads", threads);
 
   // For now we only have one thread
   SetPaused(true);
-  JSObject* threadSamples = GetPrimaryThreadProfile()->ToJSObject(aCx);
+  JSCustomObject* threadSamples = b.CreateObject();
+  GetPrimaryThreadProfile()->BuildJSObject(b, threadSamples);
   b.ArrayPush(threads, threadSamples);
   SetPaused(false);
-
-  return profile;
 }
 
 static
 void addProfileEntry(volatile StackEntry &entry, ThreadProfile &aProfile,
                      ProfileStack *stack, void *lastpc)
 {
   int lineno = -1;
 
@@ -1057,18 +1081,31 @@ void mozilla_sampler_init()
 #if defined(XP_WIN) || defined(XP_MACOSX)
                          , "stackwalk"
 #endif
                          };
   mozilla_sampler_start(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL,
                         features, sizeof(features)/sizeof(const char*));
 }
 
-void mozilla_sampler_deinit()
+void mozilla_sampler_shutdown()
 {
+  TableTicker *t = tlsTicker.get();
+  if (t) {
+    const char *val = PR_GetEnv("MOZ_PROFILER_SHUTDOWN");
+    if (val) {
+      std::ofstream stream;
+      stream.open(val);
+      if (stream.is_open()) {
+        t->ToStreamAsJSON(stream);
+        stream.close();
+      }
+    }
+  }
+
   mozilla_sampler_stop();
   // We can't delete the Stack because we can be between a
   // sampler call_enter/call_exit point.
   // TODO Need to find a safe time to delete Stack
 }
 
 void mozilla_sampler_save()
 {
--- a/tools/profiler/sps_sampler.h
+++ b/tools/profiler/sps_sampler.h
@@ -3,51 +3,52 @@
  * 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 <stdlib.h>
 #include <signal.h>
 #include <stdarg.h>
 #include "mozilla/ThreadLocal.h"
 #include "nscore.h"
-#include "jsapi.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Util.h"
 #include "nsAlgorithm.h"
 
+
 /* QT has a #define for the word "slots" and jsfriendapi.h has a struct with
  * this variable name, causing compilation problems. Alleviate this for now by
  * removing this #define */
 #ifdef MOZ_WIDGET_QT
 #undef slots
 #endif
 #include "jsfriendapi.h"
 
 using mozilla::TimeStamp;
 using mozilla::TimeDuration;
 
 struct ProfileStack;
 class TableTicker;
+class JSCustomObject;
 
 extern mozilla::ThreadLocal<ProfileStack *> tlsStack;
 extern mozilla::ThreadLocal<TableTicker *> tlsTicker;
 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
 
 #define SAMPLER_INIT() mozilla_sampler_init()
-#define SAMPLER_DEINIT() mozilla_sampler_deinit()
+#define SAMPLER_SHUTDOWN() mozilla_sampler_shutdown()
 #define SAMPLER_START(entries, interval, features, featureCount) mozilla_sampler_start(entries, interval, features, featureCount)
 #define SAMPLER_STOP() mozilla_sampler_stop()
 #define SAMPLER_IS_ACTIVE() mozilla_sampler_is_active()
 #define SAMPLER_RESPONSIVENESS(time) mozilla_sampler_responsiveness(time)
 #define SAMPLER_GET_RESPONSIVENESS() mozilla_sampler_get_responsiveness()
 #define SAMPLER_FRAME_NUMBER(frameNumber) mozilla_sampler_frame_number(frameNumber)
 #define SAMPLER_SAVE() mozilla_sampler_save()
 #define SAMPLER_GET_PROFILE() mozilla_sampler_get_profile()
@@ -167,16 +168,17 @@ bool mozilla_sampler_is_active();
 void mozilla_sampler_responsiveness(TimeStamp time);
 void mozilla_sampler_frame_number(int frameNumber);
 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();
+void mozilla_sampler_shutdown();
 
 void mozilla_sampler_print_location();
 
 namespace mozilla {
 
 class NS_STACK_CLASS SamplerStackFrameRAII {
 public:
   // we only copy the strings at save time, so to take multiple parameters we'd need to copy them then.