зеркало из https://github.com/mozilla/gecko-dev.git
Bug 867728 - Stream profiler JSON directly to a file. r=bgirard,terrence
--HG-- extra : rebase_source : 3472eb981d0c8778be318b3d51b51edfe8c1c96a
This commit is contained in:
Родитель
a51fcecd63
Коммит
549fb2406d
|
@ -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()
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче