Bug 867728 - Stream profiler JSON directly to a file. r=bgirard,terrence

--HG--
extra : rebase_source : 3472eb981d0c8778be318b3d51b51edfe8c1c96a
This commit is contained in:
Viktor Stanchev 2014-04-21 16:48:47 -04:00
Родитель a51fcecd63
Коммит 549fb2406d
25 изменённых файлов: 645 добавлений и 1076 удалений

Просмотреть файл

@ -117,8 +117,7 @@ function test_profile(aClient, aProfiler)
let location = stack.name + " (" + stack.filename + ":" + funcLine + ")";
// At least one sample is expected to have been in the busy wait above.
do_check_true(aResponse.profile.threads[0].samples.some(function(sample) {
return sample.name == "(root)" &&
typeof sample.frames == "object" &&
return typeof sample.frames == "object" &&
sample.frames.length != 0 &&
sample.frames.some(function(f) {
return (f.line == stack.lineNumber) &&

Просмотреть файл

@ -26,10 +26,6 @@
#include "UnwinderThread2.h"
#include "TableTicker.h"
// JSON
#include "JSObjectBuilder.h"
#include "nsIJSRuntimeService.h"
// Meta
#include "nsXPCOM.h"
#include "nsXPCOMCID.h"

Просмотреть файл

@ -151,6 +151,9 @@ static inline char* profiler_get_profile() { return nullptr; }
// Get the profile encoded as a JSON object.
static inline JSObject* profiler_get_profile_jsobject(JSContext* aCx) { return nullptr; }
// Get the profile and write it into a file
static inline void profiler_save_profile_to_file(char* aFilename) { }
// Get the features supported by the profiler that are accepted by profiler_init.
// Returns a null terminated char* array.
static inline char** profiler_get_features() { return nullptr; }

Просмотреть файл

@ -56,6 +56,8 @@ char* mozilla_sampler_get_profile();
JSObject *mozilla_sampler_get_profile_data(JSContext *aCx);
void mozilla_sampler_save_profile_to_file(const char* aFilename);
const char** mozilla_sampler_get_features();
void mozilla_sampler_init(void* stackTop);

Просмотреть файл

@ -143,6 +143,12 @@ JSObject* profiler_get_profile_jsobject(JSContext* aCx)
return mozilla_sampler_get_profile_data(aCx);
}
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()
{

Просмотреть файл

@ -1,318 +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 "JSCustomObjectBuilder.h"
#include "mozilla/ArrayUtils.h" // for ArrayLength
#include "nsDataHashtable.h"
#include "nsString.h"
#include "nsTArray.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) {
char16_t chr[2];
ConvertUTF8toUTF16 encoder(chr);
encoder.write(utf8CharStart, uint32_t(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() {}
~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<double>
{
static void run(std::ostream& stream, double p) {
// 13 for ms, 16 of microseconds, plus an extra 2
stream.precision(18);
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 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 (uint32_t 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 PL_DHASH_NEXT;
}
JSCustomObject::~JSCustomObject()
{
mProperties.EnumerateRead(HashTableFree, nullptr);
}
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();
}

Просмотреть файл

@ -1,65 +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 JSCUSTOMOBJECTBUILDER_H
#define JSCUSTOMOBJECTBUILDER_H
#include <ostream>
#include <stdlib.h>
#include "js/RootingAPI.h"
class JSCustomObject;
class JSCustomArray;
class JSCustomObjectBuilder
{
public:
typedef JSCustomObject* Object;
typedef JSCustomArray* Array;
typedef JSCustomObject* ObjectHandle;
typedef JSCustomArray* ArrayHandle;
typedef js::FakeRooted<JSCustomObject*> RootedObject;
typedef js::FakeRooted<JSCustomArray*> RootedArray;
// 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);
JSContext *context() const { return nullptr; }
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

Просмотреть файл

@ -1,145 +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 "jsapi.h"
#include "nsString.h"
#include "JSObjectBuilder.h"
JSObjectBuilder::JSObjectBuilder(JSContext *aCx) : mCx(aCx), mOk(true)
{}
void
JSObjectBuilder::DefineProperty(JS::HandleObject aObject, const char *name, JS::HandleObject aValue)
{
if (!mOk)
return;
mOk = JS_DefineProperty(mCx, aObject, name, aValue, JSPROP_ENUMERATE);
}
void
JSObjectBuilder::DefineProperty(JS::HandleObject aObject, const char *name, int value)
{
if (!mOk)
return;
mOk = JS_DefineProperty(mCx, aObject, name, value, JSPROP_ENUMERATE);
}
void
JSObjectBuilder::DefineProperty(JS::HandleObject aObject, const char *name, double value)
{
if (!mOk)
return;
mOk = JS_DefineProperty(mCx, aObject, name, value, JSPROP_ENUMERATE);
}
void
JSObjectBuilder::DefineProperty(JS::HandleObject aObject, const char *name, nsAString &value)
{
if (!mOk)
return;
const nsString &flat = PromiseFlatString(value);
JS::RootedString string(mCx, JS_NewUCStringCopyN(mCx, static_cast<const jschar*>(flat.get()), flat.Length()));
if (!string)
mOk = false;
if (!mOk)
return;
mOk = JS_DefineProperty(mCx, aObject, name, string, JSPROP_ENUMERATE);
}
void
JSObjectBuilder::DefineProperty(JS::HandleObject aObject, const char *name, const char *value, size_t valueLength)
{
if (!mOk)
return;
JS::RootedString string(mCx, JS_InternStringN(mCx, value, valueLength));
if (!string) {
mOk = false;
return;
}
mOk = JS_DefineProperty(mCx, aObject, name, string, JSPROP_ENUMERATE); }
void
JSObjectBuilder::DefineProperty(JS::HandleObject aObject, const char *name, const char *value)
{
DefineProperty(aObject, name, value, strlen(value));
}
void
JSObjectBuilder::ArrayPush(JS::HandleObject aArray, int value)
{
if (!mOk)
return;
uint32_t length;
mOk = JS_GetArrayLength(mCx, aArray, &length);
if (!mOk)
return;
mOk = JS_SetElement(mCx, aArray, length, value);
}
void
JSObjectBuilder::ArrayPush(JS::HandleObject aArray, const char *value)
{
if (!mOk)
return;
JS::RootedString string(mCx, JS_NewStringCopyN(mCx, value, strlen(value)));
if (!string) {
mOk = false;
return;
}
uint32_t length;
mOk = JS_GetArrayLength(mCx, aArray, &length);
if (!mOk)
return;
mOk = JS_SetElement(mCx, aArray, length, string);
}
void
JSObjectBuilder::ArrayPush(JS::HandleObject aArray, JS::HandleObject aObject)
{
if (!mOk)
return;
uint32_t length;
mOk = JS_GetArrayLength(mCx, aArray, &length);
if (!mOk)
return;
mOk = JS_SetElement(mCx, aArray, length, aObject);
}
JSObject*
JSObjectBuilder::CreateArray() {
JSObject *array = JS_NewArrayObject(mCx, 0);
if (!array)
mOk = false;
return array;
}
JSObject*
JSObjectBuilder::CreateObject() {
JSObject *obj = JS_NewObject(mCx, nullptr, JS::NullPtr(), JS::NullPtr());
if (!obj)
mOk = false;
return obj;
}

Просмотреть файл

@ -1,67 +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 JSOBJECTBUILDER_H
#define JSOBJECTBUILDER_H
#include "js/TypeDecls.h"
#include "js/RootingAPI.h"
class JSCustomArray;
class JSCustomObject;
class JSCustomObjectBuilder;
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
{
public:
typedef JS::Handle<JSObject*> ObjectHandle;
typedef JS::Handle<JSObject*> ArrayHandle;
typedef JS::Rooted<JSObject*> RootedObject;
typedef JS::Rooted<JSObject*> RootedArray;
typedef JSObject* Object;
typedef JSObject* Array;
// We need to ensure that this object lives on the stack so that GC sees it properly
explicit JSObjectBuilder(JSContext *aCx);
~JSObjectBuilder() {}
void DefineProperty(JS::HandleObject aObject, const char *name, JS::HandleObject aValue);
void DefineProperty(JS::HandleObject aObject, const char *name, int value);
void DefineProperty(JS::HandleObject aObject, const char *name, double value);
void DefineProperty(JS::HandleObject aObject, const char *name, nsAString &value);
void DefineProperty(JS::HandleObject aObject, const char *name, const char *value, size_t valueLength);
void DefineProperty(JS::HandleObject aObject, const char *name, const char *value);
void ArrayPush(JS::HandleObject aArray, int value);
void ArrayPush(JS::HandleObject aArray, const char *value);
void ArrayPush(JS::HandleObject aArray, JS::HandleObject aObject);
JSObject *CreateArray();
JSObject *CreateObject();
JSContext *context() const { return mCx; }
private:
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;
int mOk;
};
#endif

Просмотреть файл

@ -0,0 +1,180 @@
/* -*- 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 "JSStreamWriter.h"
#include "mozilla/ArrayUtils.h" // for ArrayLength
#include "nsDataHashtable.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsUTF8Utils.h"
#if _MSC_VER
#define snprintf _snprintf
#endif
#define ARRAY (void*)1
#define OBJECT (void*)2
// Escape a UTF8 string to a stream. When an illegal encoding
// is found it will insert "INVALID" and the function will return.
static 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) {
char16_t chr[2];
ConvertUTF8toUTF16 encoder(chr);
encoder.write(utf8CharStart, uint32_t(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 << "\"";
}
JSStreamWriter::JSStreamWriter(std::ostream& aStream)
: mStream(aStream)
, mNeedsComma(false)
, mNeedsName(false)
{ }
JSStreamWriter::~JSStreamWriter()
{
MOZ_ASSERT(mStack.GetSize() == 0);
}
void
JSStreamWriter::BeginObject()
{
MOZ_ASSERT(!mNeedsName);
if (mNeedsComma && mStack.Peek() == ARRAY) {
mStream << ",";
}
mStream << "{";
mNeedsComma = false;
mNeedsName = true;
mStack.Push(OBJECT);
}
void
JSStreamWriter::EndObject()
{
MOZ_ASSERT(mStack.Peek() == OBJECT);
mStream << "}";
mNeedsComma = true;
mNeedsName = false;
mStack.Pop();
if (mStack.GetSize() > 0 && mStack.Peek() == OBJECT) {
mNeedsName = true;
}
}
void
JSStreamWriter::BeginArray()
{
MOZ_ASSERT(!mNeedsName);
if (mNeedsComma && mStack.Peek() == ARRAY) {
mStream << ",";
}
mStream << "[";
mNeedsComma = false;
mStack.Push(ARRAY);
}
void
JSStreamWriter::EndArray()
{
MOZ_ASSERT(!mNeedsName);
MOZ_ASSERT(mStack.Peek() == ARRAY);
mStream << "]";
mNeedsComma = true;
mStack.Pop();
if (mStack.GetSize() > 0 && mStack.Peek() == OBJECT) {
mNeedsName = true;
}
}
void
JSStreamWriter::Name(const char *aName)
{
MOZ_ASSERT(mNeedsName);
if (mNeedsComma && mStack.Peek() == OBJECT) {
mStream << ",";
}
EscapeToStream(mStream, aName);
mStream << ":";
mNeedsName = false;
}
void
JSStreamWriter::Value(int aValue)
{
MOZ_ASSERT(!mNeedsName);
if (mNeedsComma && mStack.Peek() == ARRAY) {
mStream << ",";
}
mStream << aValue;
mNeedsComma = true;
if (mStack.Peek() == OBJECT) {
mNeedsName = true;
}
}
void
JSStreamWriter::Value(double aValue)
{
MOZ_ASSERT(!mNeedsName);
if (mNeedsComma && mStack.Peek() == ARRAY) {
mStream << ",";
}
mStream.precision(18);
mStream << aValue;
mNeedsComma = true;
if (mStack.Peek() == OBJECT) {
mNeedsName = true;
}
}
void
JSStreamWriter::Value(const char *aValue)
{
MOZ_ASSERT(!mNeedsName);
if (mNeedsComma && mStack.Peek() == ARRAY) {
mStream << ",";
}
EscapeToStream(mStream, aValue);
mNeedsComma = true;
if (mStack.Peek() == OBJECT) {
mNeedsName = true;
}
}

Просмотреть файл

@ -0,0 +1,58 @@
/* -*- 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 JSSTREAMWRITER_H
#define JSSTREAMWRITER_H
#include <ostream>
#include <stdlib.h>
#include "nsDeque.h"
class JSStreamWriter
{
public:
JSStreamWriter(std::ostream& aStream);
~JSStreamWriter();
void BeginObject();
void EndObject();
void BeginArray();
void EndArray();
void Name(const char *name);
void Value(int value);
void Value(double value);
void Value(const char *value, size_t valueLength);
void Value(const char *value);
template <typename T>
void NameValue(const char *aName, T aValue)
{
Name(aName);
Value(aValue);
}
private:
std::ostream& mStream;
bool mNeedsComma;
bool mNeedsName;
nsDeque mStack;
// This class can't be copied
JSStreamWriter(const JSStreamWriter&);
JSStreamWriter& operator=(const JSStreamWriter&);
void* operator new(size_t);
void* operator new[](size_t);
void operator delete(void*) {
// Since JSStreamWriter 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 won't know how to free this instance.
abort();
}
void operator delete[](void*);
};
#endif

Просмотреть файл

@ -4,13 +4,14 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <ostream>
#include <sstream>
#include "platform.h"
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
#include "jsapi.h"
// JSON
#include "JSObjectBuilder.h"
#include "JSCustomObjectBuilder.h"
#include "JSStreamWriter.h"
// Self
#include "ProfileEntry.h"
@ -308,138 +309,159 @@ void ThreadProfile::IterateTags(IterateTagsCallback aCallback)
void ThreadProfile::ToStreamAsJSON(std::ostream& stream)
{
JSCustomObjectBuilder b;
JSCustomObject *profile = b.CreateObject();
BuildJSObject(b, profile);
b.Serialize(profile, stream);
b.DeleteObject(profile);
JSStreamWriter b(stream);
StreamJSObject(b);
}
void ThreadProfile::StreamJSObject(JSStreamWriter& b)
{
b.BeginObject();
// Thread meta data
if (XRE_GetProcessType() == GeckoProcessType_Plugin) {
// TODO Add the proper plugin name
b.NameValue("name", "Plugin");
} else {
b.NameValue("name", mName);
}
b.NameValue("tid", static_cast<int>(mThreadId));
b.Name("samples");
b.BeginArray();
bool sample = false;
int readPos = mReadPos;
while (readPos != mLastFlushPos) {
// Number of tag consumed
ProfileEntry entry = mEntries[readPos];
switch (entry.mTagName) {
case 'r':
{
if (sample) {
b.NameValue("responsiveness", entry.mTagFloat);
}
}
break;
case 'p':
{
if (sample) {
b.NameValue("power", entry.mTagFloat);
}
}
break;
case 'f':
{
if (sample) {
b.NameValue("frameNumber", entry.mTagLine);
}
}
break;
case 't':
{
if (sample) {
b.NameValue("time", entry.mTagFloat);
}
}
break;
case 's':
{
// end the previous sample if there was one
if (sample) {
b.EndObject();
}
// begin the next sample
b.BeginObject();
sample = true;
// 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
b.Name("frames");
b.BeginArray();
b.BeginObject();
b.NameValue("location", "(root)");
b.EndObject();
int framePos = (readPos + 1) % mEntrySize;
ProfileEntry frame = mEntries[framePos];
while (framePos != mLastFlushPos && frame.mTagName != 's') {
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;
char tagBuff[DYNAMIC_MAX_STRING];
// Make sure the string is always null terminated if it fills up
// DYNAMIC_MAX_STRING-2
tagBuff[DYNAMIC_MAX_STRING-1] = '\0';
if (readAheadPos != mLastFlushPos && mEntries[readAheadPos].mTagName == 'd') {
tagStringData = processDynamicTag(framePos, &incBy, tagBuff);
}
// 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 and an optional 'n'
if (frame.mTagName == 'l') {
b.BeginObject();
// 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, DYNAMIC_MAX_STRING, "%#llx", pc);
b.NameValue("location", tagBuff);
b.EndObject();
} else if (frame.mTagName == 'c') {
b.BeginObject();
b.NameValue("location", tagStringData);
readAheadPos = (framePos + incBy) % mEntrySize;
if (readAheadPos != mLastFlushPos &&
mEntries[readAheadPos].mTagName == 'n') {
b.NameValue("line", mEntries[readAheadPos].mTagLine);
incBy++;
}
b.EndObject();
}
framePos = (framePos + incBy) % mEntrySize;
}
b.EndArray();
}
break;
}
readPos = (readPos + 1) % mEntrySize;
}
if (sample) {
b.EndObject();
}
b.EndArray();
b.Name("markers");
b.BeginArray();
readPos = mReadPos;
while (readPos != mLastFlushPos) {
ProfileEntry entry = mEntries[readPos];
if (entry.mTagName == 'm') {
entry.getMarker()->StreamJSObject(b);
}
readPos = (readPos + 1) % mEntrySize;
}
b.EndArray();
b.EndObject();
}
JSObject* ThreadProfile::ToJSObject(JSContext *aCx)
{
JSObjectBuilder b(aCx);
JS::RootedObject profile(aCx, b.CreateObject());
BuildJSObject(b, profile);
return profile;
JS::RootedValue val(aCx);
std::stringstream ss;
JSStreamWriter b(ss);
StreamJSObject(b);
NS_ConvertUTF8toUTF16 js_string(nsDependentCString(ss.str().c_str()));
JS_ParseJSON(aCx, static_cast<const jschar*>(js_string.get()), js_string.Length(), &val);
return &val.toObject();
}
template <typename Builder>
void ThreadProfile::BuildJSObject(Builder& b,
typename Builder::ObjectHandle profile)
{
// Thread meta data
if (XRE_GetProcessType() == GeckoProcessType_Plugin) {
// TODO Add the proper plugin name
b.DefineProperty(profile, "name", "Plugin");
} else {
b.DefineProperty(profile, "name", mName);
}
b.DefineProperty(profile, "tid", static_cast<int>(mThreadId));
typename Builder::RootedArray samples(b.context(), b.CreateArray());
b.DefineProperty(profile, "samples", samples);
typename Builder::RootedArray markers(b.context(), b.CreateArray());
b.DefineProperty(profile, "markers", markers);
typename Builder::RootedObject sample(b.context());
typename Builder::RootedArray frames(b.context());
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
const char* tagStringData = entry.mTagData;
int readAheadPos = (readPos + 1) % mEntrySize;
char tagBuff[DYNAMIC_MAX_STRING];
// Make sure the string is always null terminated if it fills up
// DYNAMIC_MAX_STRING-2
tagBuff[DYNAMIC_MAX_STRING-1] = '\0';
if (readAheadPos != mLastFlushPos && mEntries[readAheadPos].mTagName == 'd') {
tagStringData = processDynamicTag(readPos, &incBy, tagBuff);
}
switch (entry.mTagName) {
case 'm':
{
entry.getMarker()->BuildJSObject(b, markers);
}
break;
case 'r':
{
if (sample) {
b.DefineProperty(sample, "responsiveness", entry.mTagFloat);
}
}
break;
case 'p':
{
if (sample) {
b.DefineProperty(sample, "power", entry.mTagFloat);
}
}
break;
case 'f':
{
if (sample) {
b.DefineProperty(sample, "frameNumber", entry.mTagLine);
}
}
break;
case 't':
{
if (sample) {
b.DefineProperty(sample, "time", entry.mTagFloat);
}
}
break;
case 's':
sample = b.CreateObject();
b.DefineProperty(sample, "name", tagStringData);
frames = b.CreateArray();
b.DefineProperty(sample, "frames", frames);
b.ArrayPush(samples, sample);
// Fall though to create a label for the 's' tag
case 'c':
case 'l':
{
if (sample) {
typename Builder::RootedObject frame(b.context(), 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 {
b.DefineProperty(frame, "location", tagStringData);
readAheadPos = (readPos + incBy) % mEntrySize;
if (readAheadPos != mLastFlushPos &&
mEntries[readAheadPos].mTagName == 'n') {
b.DefineProperty(frame, "line",
mEntries[readAheadPos].mTagLine);
incBy++;
}
}
b.ArrayPush(frames, frame);
}
}
}
readPos = (readPos + incBy) % mEntrySize;
}
}
template void ThreadProfile::BuildJSObject<JSObjectBuilder>(JSObjectBuilder& b,
JS::HandleObject profile);
template void ThreadProfile::BuildJSObject<JSCustomObjectBuilder>(JSCustomObjectBuilder& b,
JSCustomObject *profile);
PseudoStack* ThreadProfile::GetPseudoStack()
{
return mPseudoStack;

Просмотреть файл

@ -10,6 +10,7 @@
#include <ostream>
#include "GeckoProfiler.h"
#include "platform.h"
#include "JSStreamWriter.h"
#include "ProfilerBacktrace.h"
#include "mozilla/Mutex.h"
@ -82,7 +83,7 @@ public:
JSObject *ToJSObject(JSContext *aCx);
PseudoStack* GetPseudoStack();
mozilla::Mutex* GetMutex();
template <typename Builder> void BuildJSObject(Builder& b, typename Builder::ObjectHandle profile);
void StreamJSObject(JSStreamWriter& b);
void BeginUnwind();
virtual void EndUnwind();
virtual SyncProfile* AsSyncProfile() { return nullptr; }

Просмотреть файл

@ -4,11 +4,11 @@
* 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 "JSObjectBuilder.h"
#include "JSStreamWriter.h"
#include "ProfilerBacktrace.h"
#include "SyncProfile.h"
ProfilerBacktrace::ProfilerBacktrace(SyncProfile* aProfile)
: mProfile(aProfile)
{
@ -22,18 +22,9 @@ ProfilerBacktrace::~ProfilerBacktrace()
}
}
template<typename Builder> void
ProfilerBacktrace::BuildJSObject(Builder& aObjBuilder,
typename Builder::ObjectHandle aScope)
void
ProfilerBacktrace::StreamJSObject(JSStreamWriter& b)
{
mozilla::MutexAutoLock lock(*mProfile->GetMutex());
mProfile->BuildJSObject(aObjBuilder, aScope);
mProfile->StreamJSObject(b);
}
template void
ProfilerBacktrace::BuildJSObject<JSCustomObjectBuilder>(
JSCustomObjectBuilder& aObjBuilder,
JSCustomObjectBuilder::ObjectHandle aScope);
template void
ProfilerBacktrace::BuildJSObject<JSObjectBuilder>(JSObjectBuilder& aObjBuilder,
JSObjectBuilder::ObjectHandle aScope);

Просмотреть файл

@ -15,8 +15,7 @@ public:
ProfilerBacktrace(SyncProfile* aProfile);
~ProfilerBacktrace();
template<typename Builder> void
BuildJSObject(Builder& aObjBuilder, typename Builder::ObjectHandle aScope);
void StreamJSObject(JSStreamWriter& b);
private:
ProfilerBacktrace(const ProfilerBacktrace&);

Просмотреть файл

@ -26,89 +26,62 @@ ProfilerMarkerPayload::~ProfilerMarkerPayload()
profiler_free_backtrace(mStack);
}
template<typename Builder> void
ProfilerMarkerPayload::prepareCommonProps(const char* aMarkerType,
Builder& aBuilder,
typename Builder::ObjectHandle aObject)
void
ProfilerMarkerPayload::streamCommonProps(const char* aMarkerType,
JSStreamWriter& b)
{
MOZ_ASSERT(aMarkerType);
aBuilder.DefineProperty(aObject, "type", aMarkerType);
b.NameValue("type", aMarkerType);
if (!mStartTime.IsNull()) {
aBuilder.DefineProperty(aObject, "startTime", profiler_time(mStartTime));
b.NameValue("startTime", profiler_time(mStartTime));
}
if (!mEndTime.IsNull()) {
aBuilder.DefineProperty(aObject, "endTime", profiler_time(mEndTime));
b.NameValue("endTime", profiler_time(mEndTime));
}
if (mStack) {
typename Builder::RootedObject stack(aBuilder.context(),
aBuilder.CreateObject());
aBuilder.DefineProperty(aObject, "stack", stack);
mStack->BuildJSObject(aBuilder, stack);
b.Name("stack");
mStack->StreamJSObject(b);
}
}
template void
ProfilerMarkerPayload::prepareCommonProps<JSCustomObjectBuilder>(
const char* aMarkerType,
JSCustomObjectBuilder& b,
JSCustomObjectBuilder::ObjectHandle aObject);
template void
ProfilerMarkerPayload::prepareCommonProps<JSObjectBuilder>(
const char* aMarkerType,
JSObjectBuilder& b,
JSObjectBuilder::ObjectHandle aObject);
ProfilerMarkerTracing::ProfilerMarkerTracing(const char* aCategory, TracingMetadata aMetaData)
: mCategory(aCategory)
, mMetaData(aMetaData)
{}
template<typename Builder>
typename Builder::Object
ProfilerMarkerTracing::preparePayloadImp(Builder& b)
void
ProfilerMarkerTracing::streamPayloadImp(JSStreamWriter& b)
{
typename Builder::RootedObject data(b.context(), b.CreateObject());
prepareCommonProps("tracing", b, data);
b.BeginObject();
streamCommonProps("tracing", b);
if (GetCategory()) {
b.DefineProperty(data, "category", GetCategory());
}
if (GetMetaData() != TRACING_DEFAULT) {
if (GetMetaData() == TRACING_INTERVAL_START) {
b.DefineProperty(data, "interval", "start");
} else if (GetMetaData() == TRACING_INTERVAL_END) {
b.DefineProperty(data, "interval", "end");
if (GetCategory()) {
b.NameValue("category", GetCategory());
}
}
return data;
if (GetMetaData() != TRACING_DEFAULT) {
if (GetMetaData() == TRACING_INTERVAL_START) {
b.NameValue("interval", "start");
} else if (GetMetaData() == TRACING_INTERVAL_END) {
b.NameValue("interval", "end");
}
}
b.EndObject();
}
template JSCustomObjectBuilder::Object
ProfilerMarkerTracing::preparePayloadImp<JSCustomObjectBuilder>(JSCustomObjectBuilder& b);
template JSObjectBuilder::Object
ProfilerMarkerTracing::preparePayloadImp<JSObjectBuilder>(JSObjectBuilder& b);
ProfilerMarkerImagePayload::ProfilerMarkerImagePayload(gfxASurface *aImg)
: mImg(aImg)
{}
template<typename Builder>
typename Builder::Object
ProfilerMarkerImagePayload::preparePayloadImp(Builder& b)
void
ProfilerMarkerImagePayload::streamPayloadImp(JSStreamWriter& b)
{
typename Builder::RootedObject data(b.context(), b.CreateObject());
prepareCommonProps("innerHTML", b, data);
// TODO: Finish me
//b.DefineProperty(data, "innerHTML", "<img src=''/>");
return data;
b.BeginObject();
streamCommonProps("innerHTML", b);
// TODO: Finish me
//b.NameValue("innerHTML", "<img src=''/>");
b.EndObject();
}
template JSCustomObjectBuilder::Object
ProfilerMarkerImagePayload::preparePayloadImp<JSCustomObjectBuilder>(JSCustomObjectBuilder& b);
template JSObjectBuilder::Object
ProfilerMarkerImagePayload::preparePayloadImp<JSObjectBuilder>(JSObjectBuilder& b);
IOMarkerPayload::IOMarkerPayload(const char* aSource,
const char* aFilename,
const mozilla::TimeStamp& aStartTime,
@ -125,23 +98,18 @@ IOMarkerPayload::~IOMarkerPayload(){
free(mFilename);
}
template<typename Builder> typename Builder::Object
IOMarkerPayload::preparePayloadImp(Builder& b)
void
IOMarkerPayload::streamPayloadImp(JSStreamWriter& b)
{
typename Builder::RootedObject data(b.context(), b.CreateObject());
prepareCommonProps("io", b, data);
b.DefineProperty(data, "source", mSource);
if (mFilename != nullptr) {
b.DefineProperty(data, "filename", mFilename);
}
return data;
b.BeginObject();
streamCommonProps("io", b);
b.NameValue("source", mSource);
if (mFilename != nullptr) {
b.NameValue("filename", mFilename);
}
b.EndObject();
}
template JSCustomObjectBuilder::Object
IOMarkerPayload::preparePayloadImp<JSCustomObjectBuilder>(JSCustomObjectBuilder& b);
template JSObjectBuilder::Object
IOMarkerPayload::preparePayloadImp<JSObjectBuilder>(JSObjectBuilder& b);
void
ProfilerJSEventMarker(const char *event)

Просмотреть файл

@ -6,8 +6,7 @@
#ifndef PROFILER_MARKERS_H
#define PROFILER_MARKERS_H
#include "JSCustomObjectBuilder.h"
#include "JSObjectBuilder.h"
#include "JSStreamWriter.h"
#include "mozilla/TimeStamp.h"
#include "nsAutoPtr.h"
@ -42,31 +41,21 @@ public:
/**
* Called from the main thread
*/
template<typename Builder>
typename Builder::Object PreparePayload(Builder& b)
{
return preparePayload(b);
void StreamPayload(JSStreamWriter& b) {
return streamPayload(b);
}
protected:
/**
* Called from the main thread
*/
template<typename Builder>
void prepareCommonProps(const char* aMarkerType, Builder& aBuilder,
typename Builder::ObjectHandle aObject);
void streamCommonProps(const char* aMarkerType, JSStreamWriter& b);
/**
* Called from the main thread
*/
virtual JSCustomObjectBuilder::Object
preparePayload(JSCustomObjectBuilder& b) = 0;
/**
* Called from the main thread
*/
virtual JSObjectBuilder::Object
preparePayload(JSObjectBuilder& b) = 0;
virtual void
streamPayload(JSStreamWriter& b) = 0;
private:
mozilla::TimeStamp mStartTime;
@ -83,14 +72,11 @@ public:
TracingMetadata GetMetaData() const { return mMetaData; }
protected:
virtual JSCustomObjectBuilder::Object
preparePayload(JSCustomObjectBuilder& b) { return preparePayloadImp(b); }
virtual JSObjectBuilder::Object
preparePayload(JSObjectBuilder& b) { return preparePayloadImp(b); }
virtual void
streamPayload(JSStreamWriter& b) { return streamPayloadImp(b); }
private:
template<typename Builder>
typename Builder::Object preparePayloadImp(Builder& b);
void streamPayloadImp(JSStreamWriter& b);
private:
const char *mCategory;
@ -105,14 +91,11 @@ public:
ProfilerMarkerImagePayload(gfxASurface *aImg);
protected:
virtual JSCustomObjectBuilder::Object
preparePayload(JSCustomObjectBuilder& b) { return preparePayloadImp(b); }
virtual JSObjectBuilder::Object
preparePayload(JSObjectBuilder& b) { return preparePayloadImp(b); }
virtual void
streamPayload(JSStreamWriter& b) { return streamPayloadImp(b); }
private:
template<typename Builder>
typename Builder::Object preparePayloadImp(Builder& b);
void streamPayloadImp(JSStreamWriter& b);
nsRefPtr<gfxASurface> mImg;
};
@ -126,14 +109,11 @@ public:
~IOMarkerPayload();
protected:
virtual JSCustomObjectBuilder::Object
preparePayload(JSCustomObjectBuilder& b) { return preparePayloadImp(b); }
virtual JSObjectBuilder::Object
preparePayload(JSObjectBuilder& b) { return preparePayloadImp(b); }
virtual void
streamPayload(JSStreamWriter& b) { return streamPayloadImp(b); }
private:
template<typename Builder>
typename Builder::Object preparePayloadImp(Builder& b);
void streamPayloadImp(JSStreamWriter& b);
const char* mSource;
char* mFilename;

Просмотреть файл

@ -110,7 +110,7 @@ public:
class ProfilerMarkerPayload;
template<typename T>
class ProfilerLinkedList;
class JSAObjectBuilder;
class JSStreamWriter;
class JSCustomArray;
class ThreadProfile;
class ProfilerMarker {
@ -126,8 +126,8 @@ public:
return mMarkerName;
}
template<typename Builder> void
BuildJSObject(Builder& b, typename Builder::ArrayHandle markers) const;
void
StreamJSObject(JSStreamWriter& b) const;
void SetGeneration(int aGenID);

Просмотреть файл

@ -6,15 +6,6 @@
#include "SaveProfileTask.h"
#include "GeckoProfiler.h"
static bool
WriteCallback(const jschar *buf, uint32_t len, void *data)
{
std::ofstream& stream = *static_cast<std::ofstream*>(data);
nsAutoCString profile = NS_ConvertUTF16toUTF8(buf, len);
stream << profile.Data();
return true;
}
nsresult
SaveProfileTask::Run() {
// Get file path
@ -39,48 +30,7 @@ SaveProfileTask::Run() {
return rv;
#endif
// Create a JSContext to run a JSObjectBuilder :(
// Based on XPCShellEnvironment
JSRuntime *rt;
JSContext *cx;
nsCOMPtr<nsIJSRuntimeService> rtsvc
= do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
if (!rtsvc || NS_FAILED(rtsvc->GetRuntime(&rt)) || !rt) {
LOG("failed to get RuntimeService");
return NS_ERROR_FAILURE;;
}
cx = JS_NewContext(rt, 8192);
if (!cx) {
LOG("Failed to get context");
return NS_ERROR_FAILURE;
}
{
JSAutoRequest ar(cx);
static const JSClass c = {
"global", JSCLASS_GLOBAL_FLAGS,
JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub,
nullptr, nullptr, nullptr, nullptr,
JS_GlobalObjectTraceHook
};
JSObject *obj = JS_NewGlobalObject(cx, &c, nullptr, JS::FireOnNewGlobalHook);
std::ofstream stream;
stream.open(tmpPath.get());
if (stream.is_open()) {
JSAutoCompartment autoComp(cx, obj);
JSObject* profileObj = profiler_get_profile_jsobject(cx);
JS::Rooted<JS::Value> val(cx, OBJECT_TO_JSVAL(profileObj));
JS_Stringify(cx, &val, JS::NullPtr(), JS::NullHandleValue, WriteCallback, &stream);
stream.close();
LOGF("Saved to %s", tmpPath.get());
} else {
LOG("Fail to open profile log file.");
}
}
JS_DestroyContext(cx);
profiler_save_profile_to_file(tmpPath.get());
return NS_OK;
}

Просмотреть файл

@ -15,9 +15,6 @@
#include "nsIJSRuntimeService.h"
#include "nsIProfileSaveEvent.h"
#include <ostream>
#include <fstream>
#ifdef XP_WIN
#include <windows.h>
#define getpid GetCurrentProcessId

Просмотреть файл

@ -21,8 +21,7 @@
#include "nsXULAppAPI.h"
// JSON
#include "JSObjectBuilder.h"
#include "JSCustomObjectBuilder.h"
#include "JSStreamWriter.h"
// Meta
#include "nsXPCOM.h"
@ -105,203 +104,209 @@ void TableTicker::HandleSaveRequest()
NS_DispatchToMainThread(runnable);
}
template <typename Builder>
typename Builder::Object TableTicker::GetMetaJSCustomObject(Builder& b)
void TableTicker::StreamMetaJSCustomObject(JSStreamWriter& b)
{
typename Builder::RootedObject meta(b.context(), b.CreateObject());
b.BeginObject();
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());
b.NameValue("version", 2);
b.NameValue("interval", interval());
b.NameValue("stackwalk", mUseStackWalk);
b.NameValue("jank", mJankOnly);
b.NameValue("processType", XRE_GetProcessType());
TimeDuration delta = TimeStamp::Now() - sStartTime;
b.DefineProperty(meta, "startTime", static_cast<float>(PR_Now()/1000.0 - delta.ToMilliseconds()));
TimeDuration delta = TimeStamp::Now() - sStartTime;
b.NameValue("startTime", static_cast<float>(PR_Now()/1000.0 - delta.ToMilliseconds()));
nsresult res;
nsCOMPtr<nsIHttpProtocolHandler> http = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &res);
if (!NS_FAILED(res)) {
nsAutoCString string;
nsresult res;
nsCOMPtr<nsIHttpProtocolHandler> http = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &res);
if (!NS_FAILED(res)) {
nsAutoCString string;
res = http->GetPlatform(string);
if (!NS_FAILED(res))
b.DefineProperty(meta, "platform", string.Data());
res = http->GetPlatform(string);
if (!NS_FAILED(res))
b.NameValue("platform", string.Data());
res = http->GetOscpu(string);
if (!NS_FAILED(res))
b.DefineProperty(meta, "oscpu", string.Data());
res = http->GetOscpu(string);
if (!NS_FAILED(res))
b.NameValue("oscpu", string.Data());
res = http->GetMisc(string);
if (!NS_FAILED(res))
b.DefineProperty(meta, "misc", string.Data());
}
res = http->GetMisc(string);
if (!NS_FAILED(res))
b.NameValue("misc", string.Data());
}
nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
if (runtime) {
nsAutoCString string;
nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
if (runtime) {
nsAutoCString string;
res = runtime->GetXPCOMABI(string);
if (!NS_FAILED(res))
b.DefineProperty(meta, "abi", string.Data());
res = runtime->GetXPCOMABI(string);
if (!NS_FAILED(res))
b.NameValue("abi", string.Data());
res = runtime->GetWidgetToolkit(string);
if (!NS_FAILED(res))
b.DefineProperty(meta, "toolkit", string.Data());
}
res = runtime->GetWidgetToolkit(string);
if (!NS_FAILED(res))
b.NameValue("toolkit", string.Data());
}
nsCOMPtr<nsIXULAppInfo> appInfo = do_GetService("@mozilla.org/xre/app-info;1");
if (appInfo) {
nsAutoCString string;
nsCOMPtr<nsIXULAppInfo> appInfo = do_GetService("@mozilla.org/xre/app-info;1");
if (appInfo) {
nsAutoCString string;
res = appInfo->GetName(string);
if (!NS_FAILED(res))
b.DefineProperty(meta, "product", string.Data());
}
res = appInfo->GetName(string);
if (!NS_FAILED(res))
b.NameValue("product", string.Data());
}
return meta;
b.EndObject();
}
void TableTicker::ToStreamAsJSON(std::ostream& stream)
{
JSCustomObjectBuilder b;
JSCustomObject* profile = b.CreateObject();
BuildJSObject(b, profile);
b.Serialize(profile, stream);
b.DeleteObject(profile);
JSStreamWriter b(stream);
StreamJSObject(b);
}
JSObject* TableTicker::ToJSObject(JSContext *aCx)
{
JSObjectBuilder b(aCx);
JS::RootedObject profile(aCx, b.CreateObject());
BuildJSObject(b, profile);
return profile;
JS::RootedValue val(aCx);
std::stringstream ss;
JSStreamWriter b(ss);
StreamJSObject(b);
NS_ConvertUTF8toUTF16 js_string(nsDependentCString(ss.str().c_str()));
JS_ParseJSON(aCx, static_cast<const jschar*>(js_string.get()), js_string.Length(), &val);
return &val.toObject();
}
template <typename Builder>
struct SubprocessClosure {
SubprocessClosure(Builder *aBuilder, typename Builder::ArrayHandle aThreads)
: mBuilder(aBuilder), mThreads(aThreads)
SubprocessClosure(JSStreamWriter *aWriter)
: mWriter(aWriter)
{}
Builder* mBuilder;
typename Builder::ArrayHandle mThreads;
JSStreamWriter* mWriter;
};
template <typename Builder>
void SubProcessCallback(const char* aProfile, void* aClosure)
{
// Called by the observer to get their profile data included
// as a sub profile
SubprocessClosure<Builder>* closure = (SubprocessClosure<Builder>*)aClosure;
SubprocessClosure* closure = (SubprocessClosure*)aClosure;
closure->mBuilder->ArrayPush(closure->mThreads, aProfile);
// Add the string profile into the profile
closure->mWriter->Value(aProfile);
}
#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
template <typename Builder>
static
typename Builder::Object BuildJavaThreadJSObject(Builder& b)
void BuildJavaThreadJSObject(JSStreamWriter& b)
{
typename Builder::RootedObject javaThread(b.context(), b.CreateObject());
b.DefineProperty(javaThread, "name", "Java Main Thread");
b.BeginObject();
typename Builder::RootedArray samples(b.context(), b.CreateArray());
b.DefineProperty(javaThread, "samples", samples);
b.NameValue("name", "Java Main Thread");
int sampleId = 0;
while (true) {
int frameId = 0;
typename Builder::RootedObject sample(b.context());
typename Builder::RootedArray frames(b.context());
while (true) {
nsCString result;
bool hasFrame = AndroidBridge::Bridge()->GetFrameNameJavaProfiling(0, sampleId, frameId, result);
if (!hasFrame) {
if (frames) {
b.DefineProperty(sample, "frames", frames);
b.Name("samples");
b.BeginArray();
// for each sample
for (int sampleId = 0; true; sampleId++) {
bool firstRun = true;
// for each frame
for (int frameId = 0; true; frameId++) {
nsCString result;
bool hasFrame = AndroidBridge::Bridge()->GetFrameNameJavaProfiling(0, sampleId, frameId, result);
// when we run out of frames, we stop looping
if (!hasFrame) {
// if we found at least one frame, we have objects to close
if (!firstRun) {
b.EndArray();
b.EndObject();
}
break;
}
// the first time around, open the sample object and frames array
if (firstRun) {
firstRun = false;
double sampleTime =
mozilla::widget::android::GeckoJavaSampler::GetSampleTimeJavaProfiling(0, sampleId);
b.BeginObject();
b.NameValue("time", sampleTime);
b.Name("frames");
b.BeginArray();
}
// add a frame to the sample
b.BeginObject();
b.NameValue("location", result.BeginReading());
b.EndObject();
}
// if we found no frames for this sample, we are done
if (firstRun) {
break;
}
break;
}
if (!sample) {
sample = b.CreateObject();
frames = b.CreateArray();
b.DefineProperty(sample, "frames", frames);
b.ArrayPush(samples, sample);
double sampleTime =
mozilla::widget::android::GeckoJavaSampler::GetSampleTimeJavaProfiling(0, sampleId);
b.DefineProperty(sample, "time", sampleTime);
}
typename Builder::RootedObject frame(b.context(), b.CreateObject());
b.DefineProperty(frame, "location", result.BeginReading());
b.ArrayPush(frames, frame);
frameId++;
}
if (frameId == 0) {
break;
}
sampleId++;
}
b.EndArray();
return javaThread;
b.EndObject();
}
#endif
template <typename Builder>
void TableTicker::BuildJSObject(Builder& b, typename Builder::ObjectHandle profile)
void TableTicker::StreamJSObject(JSStreamWriter& b)
{
// Put shared library info
b.DefineProperty(profile, "libs", GetSharedLibraryInfoString().c_str());
b.BeginObject();
// Put shared library info
b.NameValue("libs", GetSharedLibraryInfoString().c_str());
// Put meta data
typename Builder::RootedObject meta(b.context(), GetMetaJSCustomObject(b));
b.DefineProperty(profile, "meta", meta);
// Put meta data
b.Name("meta");
StreamMetaJSCustomObject(b);
// Lists the samples for each ThreadProfile
typename Builder::RootedArray threads(b.context(), b.CreateArray());
b.DefineProperty(profile, "threads", threads);
// Lists the samples for each ThreadProfile
b.Name("threads");
b.BeginArray();
SetPaused(true);
SetPaused(true);
{
mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
{
mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
// Thread not being profiled, skip it
if (!sRegisteredThreads->at(i)->Profile())
continue;
for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
// Thread not being profiled, skip it
if (!sRegisteredThreads->at(i)->Profile())
continue;
MutexAutoLock lock(*sRegisteredThreads->at(i)->Profile()->GetMutex());
MutexAutoLock lock(*sRegisteredThreads->at(i)->Profile()->GetMutex());
typename Builder::RootedObject threadSamples(b.context(), b.CreateObject());
sRegisteredThreads->at(i)->Profile()->BuildJSObject(b, threadSamples);
b.ArrayPush(threads, threadSamples);
}
}
sRegisteredThreads->at(i)->Profile()->StreamJSObject(b);
}
}
#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
if (ProfileJava()) {
mozilla::widget::android::GeckoJavaSampler::PauseJavaProfiling();
// Send a event asking any subprocesses (plugins) to
// give us their information
SubprocessClosure closure(&b);
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
if (os) {
nsRefPtr<ProfileSaveEvent> pse = new ProfileSaveEvent(SubProcessCallback, &closure);
os->NotifyObservers(pse, "profiler-subprocess", nullptr);
}
typename Builder::RootedObject javaThread(b.context(), BuildJavaThreadJSObject(b));
b.ArrayPush(threads, javaThread);
#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
if (ProfileJava()) {
mozilla::widget::android::GeckoJavaSampler::PauseJavaProfiling();
mozilla::widget::android::GeckoJavaSampler::UnpauseJavaProfiling();
}
#endif
BuildJavaThreadJSObject(b);
SetPaused(false);
mozilla::widget::android::GeckoJavaSampler::UnpauseJavaProfiling();
}
#endif
SetPaused(false);
b.EndArray();
b.EndObject();
// Send a event asking any subprocesses (plugins) to
// give us their information
SubprocessClosure<Builder> closure(&b, threads);
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
if (os) {
nsRefPtr<ProfileSaveEvent> pse = new ProfileSaveEvent(SubProcessCallback<Builder>, &closure);
os->NotifyObservers(pse, "profiler-subprocess", nullptr);
}
}
// END SaveProfileTask et al

Просмотреть файл

@ -176,8 +176,7 @@ class TableTicker: public Sampler {
void ToStreamAsJSON(std::ostream& stream);
virtual JSObject *ToJSObject(JSContext *aCx);
template <typename Builder> typename Builder::Object GetMetaJSCustomObject(Builder& b);
void StreamMetaJSCustomObject(JSStreamWriter& b);
bool HasUnwinderThread() const { return mUnwinderThread; }
bool ProfileJS() const { return mProfileJS; }
bool ProfileJava() const { return mProfileJava; }
@ -196,7 +195,7 @@ protected:
// Not implemented on platforms which do not support backtracing
void doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample);
template <typename Builder> void BuildJSObject(Builder& b, typename Builder::ObjectHandle profile);
void StreamJSObject(JSStreamWriter& b);
// This represent the application's main thread (SAMPLER_INIT)
ThreadProfile* mPrimaryThreadProfile;

Просмотреть файл

@ -24,8 +24,7 @@ if CONFIG['MOZ_ENABLE_PROFILER_SPS']:
]
UNIFIED_SOURCES += [
'BreakpadSampler.cpp',
'JSCustomObjectBuilder.cpp',
'JSObjectBuilder.cpp',
'JSStreamWriter.cpp',
'nsProfiler.cpp',
'nsProfilerFactory.cpp',
'platform.cpp',

Просмотреть файл

@ -204,10 +204,10 @@ nsProfiler::GetSharedLibraryInformation(nsAString& aOutString)
NS_IMETHODIMP nsProfiler::GetProfileData(JSContext* aCx,
JS::MutableHandle<JS::Value> aResult)
{
JSObject *obj = profiler_get_profile_jsobject(aCx);
if (!obj)
JS::RootedObject obj(aCx, profiler_get_profile_jsobject(aCx));
if (!obj) {
return NS_ERROR_FAILURE;
}
aResult.setObject(*obj);
return NS_OK;
}

Просмотреть файл

@ -132,29 +132,20 @@ ProfilerMarker::GetTime() {
return mTime;
}
template<typename Builder> void
ProfilerMarker::BuildJSObject(Builder& b, typename Builder::ArrayHandle markers) const {
typename Builder::RootedObject marker(b.context(), b.CreateObject());
b.DefineProperty(marker, "name", GetMarkerName());
// TODO: Store the callsite for this marker if available:
// if have location data
// b.DefineProperty(marker, "location", ...);
if (mPayload) {
typename Builder::RootedObject markerData(b.context(),
mPayload->PreparePayload(b));
b.DefineProperty(marker, "data", markerData);
}
b.DefineProperty(marker, "time", mTime);
b.ArrayPush(markers, marker);
void ProfilerMarker::StreamJSObject(JSStreamWriter& b) const {
b.BeginObject();
b.NameValue("name", GetMarkerName());
// TODO: Store the callsite for this marker if available:
// if have location data
// b.NameValue(marker, "location", ...);
if (mPayload) {
b.Name("data");
mPayload->StreamPayload(b);
}
b.NameValue("time", mTime);
b.EndObject();
}
template void
ProfilerMarker::BuildJSObject<JSCustomObjectBuilder>(JSCustomObjectBuilder& b,
JSCustomObjectBuilder::ArrayHandle markers) const;
template void
ProfilerMarker::BuildJSObject<JSObjectBuilder>(JSObjectBuilder& b,
JSObjectBuilder::ArrayHandle markers) const;
PendingMarkers::~PendingMarkers() {
clearMarkers();
if (mSignalLock != false) {
@ -573,6 +564,24 @@ JSObject *mozilla_sampler_get_profile_data(JSContext *aCx)
return t->ToJSObject(aCx);
}
void mozilla_sampler_save_profile_to_file(const char* aFilename)
{
TableTicker *t = tlsTicker.get();
if (!t) {
return;
}
std::ofstream stream;
stream.open(aFilename);
if (stream.is_open()) {
t->ToStreamAsJSON(stream);
stream.close();
LOGF("Saved to %s", aFilename);
} else {
LOG("Fail to open profile log file.");
}
}
const char** mozilla_sampler_get_features()
{