зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1497016 - Add an API to extract tracelogger data and use this within the gecko profiler r=mstange,djvj
Add a new class to extract tracelogger data using chunked buffers and use this to write the data out to the profiler JSON output. Copying the data in chunks lets us minimize our memory overhead when writing out to the profiler so a large array of millions of elements does not need to be allocated ahead of time. Differential Revision: https://phabricator.services.mozilla.com/D11791 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
a26b0b0040
Коммит
1d2e62c07a
|
@ -35,7 +35,8 @@
|
|||
"stackwalk",
|
||||
"tasktracer",
|
||||
"threads",
|
||||
"trackopts"
|
||||
"trackopts",
|
||||
"jstracer"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -148,6 +148,11 @@ const featureCheckboxes = [
|
|||
value: "screenshots",
|
||||
title: "Record screenshots of all browser windows.",
|
||||
},
|
||||
{
|
||||
name: "JSTracer",
|
||||
value: "jstracer",
|
||||
title: "Trace JS engine (Experimental, requires custom build.)",
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,20 +10,132 @@
|
|||
|
||||
#include "jstypes.h"
|
||||
|
||||
namespace JS {
|
||||
#ifdef JS_TRACE_LOGGING
|
||||
namespace mozilla {
|
||||
class JSONWriteFunc;
|
||||
class TimeStamp;
|
||||
}; // namespace mozilla
|
||||
|
||||
namespace JS {
|
||||
|
||||
// Used to lock any tracelogger activities, and consequently, will also block
|
||||
// any further JS execution when a thread hits an atomic tracelogger activity
|
||||
// such as payload creation.
|
||||
class AutoTraceLoggerLockGuard {
|
||||
public:
|
||||
AutoTraceLoggerLockGuard();
|
||||
~AutoTraceLoggerLockGuard();
|
||||
};
|
||||
|
||||
// An implementation type must be defined in order to gather data using the
|
||||
// TraceLoggerCollectorBuffer. Each implementation must define the type that is
|
||||
// being collected in the buffer, along with a static method that is used to
|
||||
// actually write into the buffer from the tracelogger.
|
||||
struct TraceLoggerDictionaryImpl {
|
||||
using ImplType = char;
|
||||
static size_t NextChunk(JSContext* cx, size_t* dataIndex, ImplType buffer[],
|
||||
size_t bufferSize);
|
||||
};
|
||||
|
||||
struct TraceLoggerIdImpl {
|
||||
using ImplType = uint32_t;
|
||||
static size_t NextChunk(JSContext* cx, size_t* dataIndex, ImplType buffer[],
|
||||
size_t bufferSize);
|
||||
};
|
||||
|
||||
struct TraceLoggerLineNoImpl {
|
||||
using ImplType = int32_t;
|
||||
static size_t NextChunk(JSContext* cx, size_t* dataIndex, ImplType buffer[],
|
||||
size_t bufferSize);
|
||||
};
|
||||
|
||||
struct TraceLoggerColNoImpl {
|
||||
using ImplType = int32_t;
|
||||
static size_t NextChunk(JSContext* cx, size_t* dataIndex, ImplType buffer[],
|
||||
size_t bufferSize);
|
||||
};
|
||||
|
||||
struct TraceLoggerTimeStampImpl {
|
||||
using ImplType = mozilla::TimeStamp;
|
||||
static size_t NextChunk(JSContext* cx, size_t* dataIndex, ImplType buffer[],
|
||||
size_t bufferSize);
|
||||
};
|
||||
|
||||
struct TraceLoggerDurationImpl {
|
||||
using ImplType = double;
|
||||
static size_t NextChunk(JSContext* cx, size_t* dataIndex, ImplType buffer[],
|
||||
size_t bufferSize);
|
||||
};
|
||||
|
||||
// Buffer that is used to retrieve tracelogger data in fixed size chunks so that
|
||||
// allocation of a large array is not necessary. The TraceLoggerCollectorBuffer
|
||||
// class will manage an internal state which points to the next data index being
|
||||
// collected. Each call to NextChunk will also clobber the internal buffer used
|
||||
// to store the data.
|
||||
template <class T>
|
||||
class TraceLoggerCollectorBuffer {
|
||||
using ImplType = typename T::ImplType;
|
||||
|
||||
public:
|
||||
class Iterator {
|
||||
public:
|
||||
Iterator(ImplType* buffer, size_t index)
|
||||
: iteratorIndex(index), buf(buffer) {}
|
||||
|
||||
Iterator operator++() {
|
||||
iteratorIndex++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator!=(const Iterator& other) const {
|
||||
return iteratorIndex != other.iteratorIndex;
|
||||
}
|
||||
|
||||
ImplType operator*() const { return buf[iteratorIndex]; }
|
||||
|
||||
private:
|
||||
size_t iteratorIndex;
|
||||
ImplType* buf;
|
||||
};
|
||||
|
||||
explicit TraceLoggerCollectorBuffer(AutoTraceLoggerLockGuard& lockGuard,
|
||||
JSContext* cx = nullptr,
|
||||
size_t length = 4096)
|
||||
: cx_(cx), length_(length), dataIndex_(0), bufferIndex_(0) {
|
||||
buffer_ = js_pod_malloc<ImplType>(length);
|
||||
}
|
||||
|
||||
~TraceLoggerCollectorBuffer() { js_free(buffer_); }
|
||||
|
||||
Iterator begin() const { return Iterator(buffer_, 0); }
|
||||
|
||||
Iterator end() const { return Iterator(buffer_, bufferIndex_); }
|
||||
|
||||
ImplType* internalBuffer() const { return buffer_; }
|
||||
|
||||
bool NextChunk() {
|
||||
bufferIndex_ = T::NextChunk(cx_, &dataIndex_, buffer_, length_);
|
||||
return (bufferIndex_ != 0) ? true : false;
|
||||
}
|
||||
|
||||
private:
|
||||
JSContext* cx_;
|
||||
size_t length_;
|
||||
size_t dataIndex_;
|
||||
size_t bufferIndex_;
|
||||
ImplType* buffer_;
|
||||
};
|
||||
|
||||
#ifdef JS_TRACE_LOGGING
|
||||
// Begin trace logging events. This will activate some of the
|
||||
// textId's for various events and set the global option
|
||||
// JSJITCOMPILER_ENABLE_TRACELOGGER to true.
|
||||
// This does nothing except return if the trace logger is already active.
|
||||
extern JS_PUBLIC_API void StartTraceLogger(JSContext *cx,
|
||||
mozilla::TimeStamp profilerStart);
|
||||
extern JS_PUBLIC_API void StartTraceLogger(JSContext* cx);
|
||||
|
||||
// Stop trace logging events. All textId's will be set to false, and the
|
||||
// global JSJITCOMPILER_ENABLE_TRACELOGGER will be set to false.
|
||||
// This does nothing except return if the trace logger is not active.
|
||||
extern JS_PUBLIC_API void StopTraceLogger(JSContext *cx);
|
||||
extern JS_PUBLIC_API void StopTraceLogger(JSContext* cx);
|
||||
|
||||
// Clear and free any event data that was recorded by the trace logger.
|
||||
extern JS_PUBLIC_API void ResetTraceLogger(void);
|
||||
|
@ -32,10 +144,56 @@ extern JS_PUBLIC_API void ResetTraceLogger(void);
|
|||
// Define empty inline functions for when trace logging compilation is not
|
||||
// enabled. TraceLogging.cpp will not be built in that case so we need to
|
||||
// provide something for any routines that reference these.
|
||||
inline void StartTraceLogger(JSContext *cx, mozilla::TimeStamp profilerStart) {}
|
||||
inline void StopTraceLogger(JSContext *cx) {}
|
||||
inline void StartTraceLogger(JSContext* cx) {}
|
||||
inline void StopTraceLogger(JSContext* cx) {}
|
||||
inline void ResetTraceLogger(void) {}
|
||||
inline size_t TraceLoggerDictionaryImpl::NextChunk(JSContext* cx,
|
||||
size_t* dataIndex,
|
||||
ImplType buffer[],
|
||||
size_t bufferSize) {
|
||||
return 0;
|
||||
}
|
||||
inline size_t TraceLoggerIdImpl::NextChunk(JSContext* cx, size_t* dataIndex,
|
||||
ImplType buffer[],
|
||||
size_t bufferSize) {
|
||||
return 0;
|
||||
}
|
||||
inline size_t TraceLoggerTimeStampImpl::NextChunk(JSContext* cx,
|
||||
size_t* dataIndex,
|
||||
ImplType buffer[],
|
||||
size_t bufferSize) {
|
||||
return 0;
|
||||
}
|
||||
inline size_t TraceLoggerDurationImpl::NextChunk(JSContext* cx,
|
||||
size_t* dataIndex,
|
||||
ImplType buffer[],
|
||||
size_t bufferSize) {
|
||||
return 0;
|
||||
}
|
||||
inline size_t TraceLoggerLineNoImpl::NextChunk(JSContext* cx, size_t* dataIndex,
|
||||
ImplType buffer[],
|
||||
size_t bufferSize) {
|
||||
return 0;
|
||||
}
|
||||
inline size_t TraceLoggerColNoImpl::NextChunk(JSContext* cx, size_t* dataIndex,
|
||||
ImplType buffer[],
|
||||
size_t bufferSize) {
|
||||
return 0;
|
||||
}
|
||||
inline AutoTraceLoggerLockGuard::AutoTraceLoggerLockGuard() {}
|
||||
inline AutoTraceLoggerLockGuard::~AutoTraceLoggerLockGuard() {}
|
||||
#endif
|
||||
using TraceLoggerDictionaryBuffer =
|
||||
TraceLoggerCollectorBuffer<JS::TraceLoggerDictionaryImpl>;
|
||||
using TraceLoggerIdBuffer = TraceLoggerCollectorBuffer<JS::TraceLoggerIdImpl>;
|
||||
using TraceLoggerTimeStampBuffer =
|
||||
TraceLoggerCollectorBuffer<JS::TraceLoggerTimeStampImpl>;
|
||||
using TraceLoggerDurationBuffer =
|
||||
TraceLoggerCollectorBuffer<JS::TraceLoggerDurationImpl>;
|
||||
using TraceLoggerLineNoBuffer =
|
||||
TraceLoggerCollectorBuffer<JS::TraceLoggerLineNoImpl>;
|
||||
using TraceLoggerColNoBuffer =
|
||||
TraceLoggerCollectorBuffer<JS::TraceLoggerColNoImpl>;
|
||||
}; // namespace JS
|
||||
|
||||
#endif /* js_TraceLoggerAPI_h */
|
||||
|
|
|
@ -636,6 +636,278 @@ void TraceLoggerThread::stopEvent(uint32_t id) {
|
|||
log(TraceLogger_Stop);
|
||||
}
|
||||
|
||||
JS::AutoTraceLoggerLockGuard::AutoTraceLoggerLockGuard() {
|
||||
traceLoggerState->lock.lock();
|
||||
}
|
||||
|
||||
JS::AutoTraceLoggerLockGuard::~AutoTraceLoggerLockGuard() {
|
||||
traceLoggerState->lock.unlock();
|
||||
}
|
||||
|
||||
size_t JS::TraceLoggerDictionaryImpl::NextChunk(JSContext* cx,
|
||||
size_t* dataIndex,
|
||||
char buffer[],
|
||||
size_t bufferSize) {
|
||||
MOZ_ASSERT(dataIndex != nullptr);
|
||||
if (!traceLoggerState || bufferSize == 0 || !buffer ||
|
||||
!jit::JitOptions.enableTraceLogger) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t bufferIndex = 0;
|
||||
|
||||
const char* eventString = nullptr;
|
||||
if (*dataIndex < TraceLogger_Last) {
|
||||
eventString = TLTextIdString(static_cast<TraceLoggerTextId>(*dataIndex));
|
||||
} else {
|
||||
uint32_t dictId = *dataIndex - TraceLogger_Last;
|
||||
if (dictId < traceLoggerState->dictionaryData.length()) {
|
||||
eventString = traceLoggerState->dictionaryData[dictId].get();
|
||||
MOZ_ASSERT(eventString);
|
||||
}
|
||||
}
|
||||
|
||||
if (eventString) {
|
||||
size_t length = strlen(eventString);
|
||||
if (length < bufferSize - 1) {
|
||||
memcpy(buffer, eventString, length);
|
||||
buffer[length] = '\0';
|
||||
bufferIndex = length;
|
||||
} else {
|
||||
memcpy(buffer, eventString, bufferSize);
|
||||
buffer[bufferSize - 1] = '\0';
|
||||
bufferIndex = bufferSize - 1;
|
||||
}
|
||||
}
|
||||
|
||||
(*dataIndex)++;
|
||||
return bufferIndex;
|
||||
}
|
||||
|
||||
size_t JS::TraceLoggerIdImpl::NextChunk(JSContext* cx, size_t* dataIndex,
|
||||
uint32_t buffer[], size_t bufferSize) {
|
||||
MOZ_ASSERT(dataIndex != nullptr);
|
||||
if (!cx || !cx->traceLogger) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bufferSize == 0 || !buffer || !jit::JitOptions.enableTraceLogger) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t bufferIndex = 0;
|
||||
ContinuousSpace<EventEntry>& events = cx->traceLogger->events;
|
||||
|
||||
for (; *dataIndex < events.size(); (*dataIndex)++) {
|
||||
if (TLTextIdIsInternalEvent(events[*dataIndex].textId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (events[*dataIndex].textId >= TraceLogger_Last) {
|
||||
TraceLoggerEventPayload* payload =
|
||||
traceLoggerState->getPayload(events[*dataIndex].textId);
|
||||
MOZ_ASSERT(payload);
|
||||
// Write the index of this event into the jsTracerDictionary array
|
||||
// property
|
||||
uint32_t dictId = TraceLogger_Last + payload->dictionaryId();
|
||||
buffer[bufferIndex++] = dictId;
|
||||
payload->release();
|
||||
} else {
|
||||
buffer[bufferIndex++] = events[*dataIndex].textId;
|
||||
;
|
||||
}
|
||||
|
||||
if (bufferIndex == bufferSize) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return bufferIndex;
|
||||
}
|
||||
|
||||
size_t JS::TraceLoggerLineNoImpl::NextChunk(JSContext* cx, size_t* dataIndex,
|
||||
int32_t buffer[],
|
||||
size_t bufferSize) {
|
||||
MOZ_ASSERT(dataIndex != nullptr);
|
||||
if (!cx || !cx->traceLogger) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bufferSize == 0 || !buffer || !jit::JitOptions.enableTraceLogger) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t bufferIndex = 0;
|
||||
ContinuousSpace<EventEntry>& events = cx->traceLogger->events;
|
||||
|
||||
for (; *dataIndex < events.size(); (*dataIndex)++) {
|
||||
if (TLTextIdIsInternalEvent(events[*dataIndex].textId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (events[*dataIndex].textId >= TraceLogger_Last) {
|
||||
TraceLoggerEventPayload* payload =
|
||||
traceLoggerState->getPayload(events[*dataIndex].textId);
|
||||
MOZ_ASSERT(payload);
|
||||
mozilla::Maybe<uint32_t> line = payload->line();
|
||||
payload->release();
|
||||
if (line) {
|
||||
buffer[bufferIndex++] = *line;
|
||||
} else {
|
||||
buffer[bufferIndex++] = -1;
|
||||
}
|
||||
} else {
|
||||
buffer[bufferIndex++] = -1;
|
||||
}
|
||||
if (bufferIndex == bufferSize) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return bufferIndex;
|
||||
}
|
||||
|
||||
size_t JS::TraceLoggerColNoImpl::NextChunk(JSContext* cx, size_t* dataIndex,
|
||||
int32_t buffer[],
|
||||
size_t bufferSize) {
|
||||
MOZ_ASSERT(dataIndex != nullptr);
|
||||
if (!cx || !cx->traceLogger) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bufferSize == 0 || !buffer || !jit::JitOptions.enableTraceLogger) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t bufferIndex = 0;
|
||||
ContinuousSpace<EventEntry>& events = cx->traceLogger->events;
|
||||
|
||||
for (; *dataIndex < events.size(); (*dataIndex)++) {
|
||||
if (TLTextIdIsInternalEvent(events[*dataIndex].textId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (events[*dataIndex].textId >= TraceLogger_Last) {
|
||||
TraceLoggerEventPayload* payload =
|
||||
traceLoggerState->getPayload(events[*dataIndex].textId);
|
||||
MOZ_ASSERT(payload);
|
||||
mozilla::Maybe<uint32_t> column = payload->column();
|
||||
payload->release();
|
||||
if (column) {
|
||||
buffer[bufferIndex++] = *column;
|
||||
} else {
|
||||
buffer[bufferIndex++] = -1;
|
||||
}
|
||||
} else {
|
||||
buffer[bufferIndex++] = -1;
|
||||
}
|
||||
if (bufferIndex == bufferSize) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return bufferIndex;
|
||||
}
|
||||
|
||||
size_t JS::TraceLoggerTimeStampImpl::NextChunk(JSContext* cx, size_t* dataIndex,
|
||||
mozilla::TimeStamp buffer[],
|
||||
size_t bufferSize) {
|
||||
MOZ_ASSERT(dataIndex != nullptr);
|
||||
if (!cx || !cx->traceLogger) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bufferSize == 0 || !buffer || !jit::JitOptions.enableTraceLogger) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t bufferIndex = 0;
|
||||
ContinuousSpace<EventEntry>& events = cx->traceLogger->events;
|
||||
|
||||
for (; *dataIndex < events.size(); (*dataIndex)++) {
|
||||
if (TLTextIdIsInternalEvent(events[*dataIndex].textId)) {
|
||||
continue;
|
||||
}
|
||||
buffer[bufferIndex++] = events[*dataIndex].time;
|
||||
if (bufferIndex == bufferSize) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return bufferIndex;
|
||||
}
|
||||
|
||||
size_t JS::TraceLoggerDurationImpl::NextChunk(JSContext* cx, size_t* dataIndex,
|
||||
double buffer[],
|
||||
size_t bufferSize) {
|
||||
MOZ_ASSERT(dataIndex != nullptr);
|
||||
if (!cx || !cx->traceLogger) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bufferSize == 0 || !buffer || !jit::JitOptions.enableTraceLogger) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ContinuousSpace<EventEntry>& events = cx->traceLogger->events;
|
||||
Vector<size_t, 0, js::SystemAllocPolicy> eventStack;
|
||||
using EventDurationMap =
|
||||
HashMap<size_t, double, DefaultHasher<size_t>, SystemAllocPolicy>;
|
||||
EventDurationMap eventMap;
|
||||
|
||||
size_t bufferIndex = 0;
|
||||
for (; *dataIndex < events.size(); (*dataIndex)++) {
|
||||
if (TLTextIdIsInternalEvent(events[*dataIndex].textId)) {
|
||||
continue;
|
||||
}
|
||||
double duration = 0;
|
||||
if (TLTextIdIsLogEvent(events[*dataIndex].textId)) {
|
||||
// log events are snapshot events with no start & stop
|
||||
duration = -1;
|
||||
} else if (EventDurationMap::Ptr p = eventMap.lookup(*dataIndex)) {
|
||||
// value has already been cached
|
||||
duration = p->value();
|
||||
} else {
|
||||
MOZ_ASSERT(eventStack.empty());
|
||||
if (!eventStack.append(*dataIndex)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Search through the events array to find the matching stop event in
|
||||
// order to calculate the duration time. Cache all other durations we
|
||||
// calculate in the meantime.
|
||||
for (size_t j = *dataIndex + 1; j < events.size(); j++) {
|
||||
uint32_t id = events[j].textId;
|
||||
if (id == TraceLogger_Stop) {
|
||||
uint32_t prev = eventStack.popCopy();
|
||||
double delta = (events[j].time - events[prev].time).ToMicroseconds();
|
||||
if (prev == *dataIndex) {
|
||||
MOZ_ASSERT(eventStack.empty());
|
||||
duration = delta;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!eventMap.putNew(prev, delta)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
} else if (TLTextIdIsTreeEvent(id)) {
|
||||
if (!eventStack.append(j)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffer[bufferIndex++] = duration;
|
||||
if (bufferIndex == bufferSize) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return bufferIndex;
|
||||
}
|
||||
|
||||
void TraceLoggerThread::logTimestamp(TraceLoggerTextId id) {
|
||||
logTimestamp(uint32_t(id));
|
||||
}
|
||||
|
@ -1109,33 +1381,36 @@ TraceLoggerEvent::TraceLoggerEvent(const TraceLoggerEvent& other)
|
|||
|
||||
JS_PUBLIC_API void JS::ResetTraceLogger(void) { js::ResetTraceLogger(); }
|
||||
|
||||
JS_PUBLIC_API void JS::StartTraceLogger(JSContext* cx,
|
||||
mozilla::TimeStamp profilerStart) {
|
||||
if (jit::JitOptions.enableTraceLogger || !traceLoggerState) {
|
||||
JS_PUBLIC_API void JS::StartTraceLogger(JSContext* cx) {
|
||||
if (!EnsureTraceLoggerState()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LockGuard<Mutex> guard(traceLoggerState->lock);
|
||||
traceLoggerState->enableTextIdsForProfiler();
|
||||
jit::JitOptions.enableTraceLogger = true;
|
||||
if (!jit::JitOptions.enableTraceLogger) {
|
||||
LockGuard<Mutex> guard(traceLoggerState->lock);
|
||||
traceLoggerState->enableTextIdsForProfiler();
|
||||
jit::JitOptions.enableTraceLogger = true;
|
||||
}
|
||||
|
||||
// Reset the start time to profile start so it aligns with sampling.
|
||||
traceLoggerState->startTime = profilerStart;
|
||||
|
||||
if (cx->traceLogger) {
|
||||
cx->traceLogger->enable();
|
||||
TraceLoggerThread* logger = traceLoggerState->forCurrentThread(cx);
|
||||
if (logger) {
|
||||
logger->enable();
|
||||
}
|
||||
}
|
||||
|
||||
JS_PUBLIC_API void JS::StopTraceLogger(JSContext* cx) {
|
||||
if (!jit::JitOptions.enableTraceLogger || !traceLoggerState) {
|
||||
if (!traceLoggerState) {
|
||||
return;
|
||||
}
|
||||
|
||||
LockGuard<Mutex> guard(traceLoggerState->lock);
|
||||
traceLoggerState->disableTextIdsForProfiler();
|
||||
jit::JitOptions.enableTraceLogger = false;
|
||||
if (cx->traceLogger) {
|
||||
cx->traceLogger->disable();
|
||||
if (jit::JitOptions.enableTraceLogger) {
|
||||
LockGuard<Mutex> guard(traceLoggerState->lock);
|
||||
traceLoggerState->disableTextIdsForProfiler();
|
||||
jit::JitOptions.enableTraceLogger = false;
|
||||
}
|
||||
|
||||
TraceLoggerThread* logger = traceLoggerState->forCurrentThread(cx);
|
||||
if (logger) {
|
||||
logger->disable();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "js/AllocPolicy.h"
|
||||
#include "js/HashTable.h"
|
||||
#include "js/TraceLoggerAPI.h"
|
||||
#include "js/TypeDecls.h"
|
||||
#include "js/Vector.h"
|
||||
#include "threading/LockGuard.h"
|
||||
|
@ -218,6 +219,12 @@ class TraceLoggerEventPayload {
|
|||
// Per thread trace logger state.
|
||||
class TraceLoggerThread : public mozilla::LinkedListElement<TraceLoggerThread> {
|
||||
#ifdef JS_TRACE_LOGGING
|
||||
friend JS::TraceLoggerIdImpl;
|
||||
friend JS::TraceLoggerTimeStampImpl;
|
||||
friend JS::TraceLoggerDurationImpl;
|
||||
friend JS::TraceLoggerLineNoImpl;
|
||||
friend JS::TraceLoggerColNoImpl;
|
||||
|
||||
private:
|
||||
uint32_t enabled_;
|
||||
bool failed;
|
||||
|
@ -345,6 +352,7 @@ class TraceLoggerThread : public mozilla::LinkedListElement<TraceLoggerThread> {
|
|||
// Process wide trace logger state.
|
||||
class TraceLoggerThreadState {
|
||||
#ifdef JS_TRACE_LOGGING
|
||||
friend JS::TraceLoggerDictionaryImpl;
|
||||
#ifdef DEBUG
|
||||
bool initialized;
|
||||
#endif
|
||||
|
|
|
@ -104,7 +104,8 @@ inline const char* TLTextIdString(TraceLoggerTextId id) {
|
|||
case TraceLogger_Error:
|
||||
return "TraceLogger failed to process text";
|
||||
case TraceLogger_Internal:
|
||||
return "TraceLogger overhead";
|
||||
case TraceLogger_TreeItemEnd:
|
||||
return "TraceLogger internal event";
|
||||
#define NAME(textId) \
|
||||
case TraceLogger_##textId: \
|
||||
return #textId;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ProfiledThreadData.h"
|
||||
#include "js/TraceLoggerAPI.h"
|
||||
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
|
||||
|
@ -37,7 +38,7 @@ void ProfiledThreadData::StreamJSON(const ProfileBuffer& aBuffer,
|
|||
JSContext* aCx,
|
||||
SpliceableJSONWriter& aWriter,
|
||||
const TimeStamp& aProcessStartTime,
|
||||
double aSinceTime) {
|
||||
double aSinceTime, bool JSTracerEnabled) {
|
||||
if (mJITFrameInfoForPreviousJSContexts &&
|
||||
mJITFrameInfoForPreviousJSContexts->HasExpired(aBuffer.mRangeStart)) {
|
||||
mJITFrameInfoForPreviousJSContexts = nullptr;
|
||||
|
@ -102,9 +103,101 @@ void ProfiledThreadData::StreamJSON(const ProfileBuffer& aBuffer,
|
|||
aWriter.EndArray();
|
||||
}
|
||||
|
||||
if (aCx && JSTracerEnabled) {
|
||||
StreamTraceLoggerJSON(aCx, aWriter, aProcessStartTime);
|
||||
}
|
||||
|
||||
aWriter.End();
|
||||
}
|
||||
|
||||
void ProfiledThreadData::StreamTraceLoggerJSON(
|
||||
JSContext* aCx, SpliceableJSONWriter& aWriter,
|
||||
const TimeStamp& aProcessStartTime) {
|
||||
aWriter.StartObjectProperty("jsTracerEvents");
|
||||
{
|
||||
JS::AutoTraceLoggerLockGuard lockGuard;
|
||||
uint32_t length = 0;
|
||||
|
||||
// Collect Event Ids
|
||||
aWriter.StartArrayProperty("events", mozilla::JSONWriter::SingleLineStyle);
|
||||
{
|
||||
JS::TraceLoggerIdBuffer collectionBuffer(lockGuard, aCx);
|
||||
while (collectionBuffer.NextChunk()) {
|
||||
for (uint32_t val : collectionBuffer) {
|
||||
aWriter.IntElement(val);
|
||||
length++;
|
||||
}
|
||||
}
|
||||
}
|
||||
aWriter.EndArray();
|
||||
|
||||
// Collect Event Timestamps
|
||||
aWriter.StartArrayProperty("timestamps",
|
||||
mozilla::JSONWriter::SingleLineStyle);
|
||||
{
|
||||
JS::TraceLoggerTimeStampBuffer collectionBuffer(lockGuard, aCx);
|
||||
while (collectionBuffer.NextChunk()) {
|
||||
for (TimeStamp val : collectionBuffer) {
|
||||
aWriter.DoubleElement((val - aProcessStartTime).ToMicroseconds());
|
||||
}
|
||||
}
|
||||
}
|
||||
aWriter.EndArray();
|
||||
|
||||
// Collect Event Durations
|
||||
aWriter.StartArrayProperty("durations",
|
||||
mozilla::JSONWriter::SingleLineStyle);
|
||||
{
|
||||
JS::TraceLoggerDurationBuffer collectionBuffer(lockGuard, aCx);
|
||||
while (collectionBuffer.NextChunk()) {
|
||||
for (double val : collectionBuffer) {
|
||||
if (val == -1) {
|
||||
aWriter.NullElement();
|
||||
} else {
|
||||
aWriter.DoubleElement(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
aWriter.EndArray();
|
||||
|
||||
// Collect Event LineNo
|
||||
aWriter.StartArrayProperty("line", mozilla::JSONWriter::SingleLineStyle);
|
||||
{
|
||||
JS::TraceLoggerLineNoBuffer collectionBuffer(lockGuard, aCx);
|
||||
while (collectionBuffer.NextChunk()) {
|
||||
for (int32_t val : collectionBuffer) {
|
||||
if (val == -1) {
|
||||
aWriter.NullElement();
|
||||
} else {
|
||||
aWriter.IntElement(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
aWriter.EndArray();
|
||||
|
||||
// Collect Event ColNo
|
||||
aWriter.StartArrayProperty("column", mozilla::JSONWriter::SingleLineStyle);
|
||||
{
|
||||
JS::TraceLoggerColNoBuffer collectionBuffer(lockGuard, aCx);
|
||||
while (collectionBuffer.NextChunk()) {
|
||||
for (int32_t val : collectionBuffer) {
|
||||
if (val == -1) {
|
||||
aWriter.NullElement();
|
||||
} else {
|
||||
aWriter.IntElement(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
aWriter.EndArray();
|
||||
|
||||
aWriter.IntProperty("length", length);
|
||||
}
|
||||
aWriter.EndObject();
|
||||
}
|
||||
|
||||
void StreamSamplesAndMarkers(const char* aName, int aThreadId,
|
||||
const ProfileBuffer& aBuffer,
|
||||
SpliceableJSONWriter& aWriter,
|
||||
|
|
|
@ -63,7 +63,10 @@ class ProfiledThreadData final {
|
|||
void StreamJSON(const ProfileBuffer& aBuffer, JSContext* aCx,
|
||||
SpliceableJSONWriter& aWriter,
|
||||
const mozilla::TimeStamp& aProcessStartTime,
|
||||
double aSinceTime);
|
||||
double aSinceTime, bool aJSTracerEnabled);
|
||||
|
||||
void StreamTraceLoggerJSON(JSContext* aCx, SpliceableJSONWriter& aWriter,
|
||||
const TimeStamp& aProcessStartTime);
|
||||
|
||||
// Returns nullptr if this is not the main thread, the responsiveness
|
||||
// feature is not turned on, or if this thread is not being profiled.
|
||||
|
|
|
@ -15,7 +15,7 @@ RegisteredThread::RegisteredThread(ThreadInfo* aInfo, nsIEventTarget* aThread,
|
|||
mThread(aThread),
|
||||
mContext(nullptr),
|
||||
mJSSampling(INACTIVE),
|
||||
mJSTrackOptimizations(false) {
|
||||
mJSFlags(0) {
|
||||
MOZ_COUNT_CTOR(RegisteredThread);
|
||||
|
||||
// We don't have to guess on mac
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "platform.h"
|
||||
#include "ThreadInfo.h"
|
||||
#include "js/TraceLoggerAPI.h"
|
||||
|
||||
// This class contains the state for a single thread that is accessible without
|
||||
// protection from gPSMutex in platform.cpp. Because there is no external
|
||||
|
@ -186,8 +187,6 @@ class RegisteredThread final {
|
|||
// important that the JS engine doesn't touch this once the thread dies.
|
||||
js::SetContextProfilingStack(aContext,
|
||||
&RacyRegisteredThread().ProfilingStack());
|
||||
|
||||
PollJSSampling();
|
||||
}
|
||||
|
||||
void ClearJSContext() {
|
||||
|
@ -203,13 +202,13 @@ class RegisteredThread final {
|
|||
// Request that this thread start JS sampling. JS sampling won't actually
|
||||
// start until a subsequent PollJSSampling() call occurs *and* mContext has
|
||||
// been set.
|
||||
void StartJSSampling(bool aTrackOptimizations) {
|
||||
void StartJSSampling(uint32_t aJSFlags) {
|
||||
// This function runs on-thread or off-thread.
|
||||
|
||||
MOZ_RELEASE_ASSERT(mJSSampling == INACTIVE ||
|
||||
mJSSampling == INACTIVE_REQUESTED);
|
||||
mJSSampling = ACTIVE_REQUESTED;
|
||||
mJSTrackOptimizations = aTrackOptimizations;
|
||||
mJSFlags = aJSFlags;
|
||||
}
|
||||
|
||||
// Request that this thread stop JS sampling. JS sampling won't actually stop
|
||||
|
@ -240,13 +239,20 @@ class RegisteredThread final {
|
|||
if (mJSSampling == ACTIVE_REQUESTED) {
|
||||
mJSSampling = ACTIVE;
|
||||
js::EnableContextProfilingStack(mContext, true);
|
||||
JS_SetGlobalJitCompilerOption(
|
||||
mContext, JSJITCOMPILER_TRACK_OPTIMIZATIONS, mJSTrackOptimizations);
|
||||
JS_SetGlobalJitCompilerOption(mContext,
|
||||
JSJITCOMPILER_TRACK_OPTIMIZATIONS,
|
||||
TrackOptimizationsEnabled());
|
||||
if (JSTracerEnabled()) {
|
||||
JS::StartTraceLogger(mContext);
|
||||
}
|
||||
js::RegisterContextProfilingEventMarker(mContext, profiler_add_marker);
|
||||
|
||||
} else if (mJSSampling == INACTIVE_REQUESTED) {
|
||||
mJSSampling = INACTIVE;
|
||||
js::EnableContextProfilingStack(mContext, false);
|
||||
if (JSTracerEnabled()) {
|
||||
JS::StopTraceLogger(mContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -311,7 +317,15 @@ class RegisteredThread final {
|
|||
INACTIVE_REQUESTED = 3,
|
||||
} mJSSampling;
|
||||
|
||||
bool mJSTrackOptimizations;
|
||||
uint32_t mJSFlags;
|
||||
|
||||
bool TrackOptimizationsEnabled() {
|
||||
return mJSFlags & uint32_t(JSSamplingFlags::TrackOptimizations);
|
||||
}
|
||||
|
||||
bool JSTracerEnabled() {
|
||||
return mJSFlags & uint32_t(JSSamplingFlags::TraceLogging);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // RegisteredThread_h
|
||||
|
|
|
@ -73,6 +73,7 @@
|
|||
#include "shared-libraries.h"
|
||||
#include "prdtoa.h"
|
||||
#include "prtime.h"
|
||||
#include "js/TraceLoggerAPI.h"
|
||||
|
||||
#if defined(XP_WIN)
|
||||
#include <processthreadsapi.h> // for GetCurrentProcessId()
|
||||
|
@ -590,6 +591,17 @@ class ActivePS {
|
|||
|
||||
#undef PS_GET_FEATURE
|
||||
|
||||
static uint32_t JSFlags(PSLockRef aLock) {
|
||||
uint32_t Flags = 0;
|
||||
Flags |= FeatureJS(aLock) ? uint32_t(JSSamplingFlags::StackSampling) : 0;
|
||||
Flags |= FeatureTrackOptimizations(aLock)
|
||||
? uint32_t(JSSamplingFlags::TrackOptimizations)
|
||||
: 0;
|
||||
Flags |=
|
||||
FeatureJSTracer(aLock) ? uint32_t(JSSamplingFlags::TraceLogging) : 0;
|
||||
return Flags;
|
||||
}
|
||||
|
||||
PS_GET(const Vector<std::string>&, Filters)
|
||||
|
||||
static ProfileBuffer& Buffer(PSLockRef) { return *sInstance->mBuffer.get(); }
|
||||
|
@ -1964,7 +1976,8 @@ static void locked_profiler_stream_json_for_this_process(
|
|||
registeredThread ? registeredThread->GetJSContext() : nullptr;
|
||||
ProfiledThreadData* profiledThreadData = thread.second();
|
||||
profiledThreadData->StreamJSON(buffer, cx, aWriter,
|
||||
CorePS::ProcessStartTime(), aSinceTime);
|
||||
CorePS::ProcessStartTime(), aSinceTime,
|
||||
ActivePS::FeatureJSTracer(aLock));
|
||||
}
|
||||
|
||||
#if defined(GP_OS_android)
|
||||
|
@ -1980,7 +1993,8 @@ static void locked_profiler_stream_json_for_this_process(
|
|||
ProfiledThreadData profiledThreadData(
|
||||
threadInfo, nullptr, ActivePS::FeatureResponsiveness(aLock));
|
||||
profiledThreadData.StreamJSON(*javaBuffer.get(), nullptr, aWriter,
|
||||
CorePS::ProcessStartTime(), aSinceTime);
|
||||
CorePS::ProcessStartTime(), aSinceTime,
|
||||
ActivePS::FeatureJSTracer(aLock));
|
||||
|
||||
java::GeckoJavaSampler::Unpause();
|
||||
}
|
||||
|
@ -1988,6 +2002,19 @@ static void locked_profiler_stream_json_for_this_process(
|
|||
}
|
||||
aWriter.EndArray();
|
||||
|
||||
if (ActivePS::FeatureJSTracer(aLock)) {
|
||||
aWriter.StartArrayProperty("jsTracerDictionary");
|
||||
{
|
||||
JS::AutoTraceLoggerLockGuard lockGuard;
|
||||
// Collect Event Dictionary
|
||||
JS::TraceLoggerDictionaryBuffer collectionBuffer(lockGuard);
|
||||
while (collectionBuffer.NextChunk()) {
|
||||
aWriter.StringElement(collectionBuffer.internalBuffer());
|
||||
}
|
||||
}
|
||||
aWriter.EndArray();
|
||||
}
|
||||
|
||||
aWriter.StartArrayProperty("pausedRanges");
|
||||
{ buffer.StreamPausedRangesToJSON(aWriter, aSinceTime); }
|
||||
aWriter.EndArray();
|
||||
|
@ -2492,8 +2519,7 @@ static ProfilingStack* locked_register_thread(PSLockRef aLock,
|
|||
if (ActivePS::FeatureJS(aLock)) {
|
||||
// This StartJSSampling() call is on-thread, so we can poll manually to
|
||||
// start JS sampling immediately.
|
||||
registeredThread->StartJSSampling(
|
||||
ActivePS::FeatureTrackOptimizations(aLock));
|
||||
registeredThread->StartJSSampling(ActivePS::JSFlags(aLock));
|
||||
registeredThread->PollJSSampling();
|
||||
if (registeredThread->GetJSContext()) {
|
||||
profiledThreadData->NotifyReceivedJSContext(
|
||||
|
@ -3115,8 +3141,7 @@ static void locked_profiler_start(PSLockRef aLock, uint32_t aCapacity,
|
|||
MakeUnique<ProfiledThreadData>(
|
||||
info, eventTarget, ActivePS::FeatureResponsiveness(aLock)));
|
||||
if (ActivePS::FeatureJS(aLock)) {
|
||||
registeredThread->StartJSSampling(
|
||||
ActivePS::FeatureTrackOptimizations(aLock));
|
||||
registeredThread->StartJSSampling(ActivePS::JSFlags(aLock));
|
||||
if (info->ThreadId() == tid) {
|
||||
// We can manually poll the current thread so it starts sampling
|
||||
// immediately.
|
||||
|
@ -3825,8 +3850,7 @@ void profiler_clear_js_context() {
|
|||
|
||||
// Tell the thread that we'd like to have JS sampling on this
|
||||
// thread again, once it gets a new JSContext (if ever).
|
||||
registeredThread->StartJSSampling(
|
||||
ActivePS::FeatureTrackOptimizations(lock));
|
||||
registeredThread->StartJSSampling(ActivePS::JSFlags(lock));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,4 +116,11 @@ void AppendSharedLibraries(mozilla::JSONWriter& aWriter);
|
|||
uint32_t ParseFeaturesFromStringArray(const char** aFeatures,
|
||||
uint32_t aFeatureCount);
|
||||
|
||||
// Flags to conveniently track various JS features.
|
||||
enum class JSSamplingFlags {
|
||||
StackSampling = 0x1,
|
||||
TrackOptimizations = 0x2,
|
||||
TraceLogging = 0x4
|
||||
};
|
||||
|
||||
#endif /* ndef TOOLS_PLATFORM_H_ */
|
||||
|
|
|
@ -160,7 +160,10 @@ class TimeStamp;
|
|||
MACRO(11, "threads", Threads) \
|
||||
\
|
||||
/* Have the JavaScript engine track JIT optimizations. */ \
|
||||
MACRO(12, "trackopts", TrackOptimizations)
|
||||
MACRO(12, "trackopts", TrackOptimizations) \
|
||||
\
|
||||
/* Enable tracing of the JavaScript engine. */ \
|
||||
MACRO(13, "jstracer", JSTracer)
|
||||
|
||||
struct ProfilerFeature {
|
||||
#define DECLARE(n_, str_, Name_) \
|
||||
|
|
Загрузка…
Ссылка в новой задаче