xpcom/base/VisualEventTracer.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/xpcom/base/VisualEventTracer.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,681 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +#include "mozilla/VisualEventTracer.h"
     1.9 +#include "mozilla/Monitor.h"
    1.10 +#include "mozilla/TimeStamp.h"
    1.11 +#include "nscore.h"
    1.12 +#include "prthread.h"
    1.13 +#include "prprf.h"
    1.14 +#include "prenv.h"
    1.15 +#include "plstr.h"
    1.16 +#include "nsThreadUtils.h"
    1.17 +
    1.18 +namespace mozilla { namespace eventtracer {
    1.19 +
    1.20 +#ifdef MOZ_VISUAL_EVENT_TRACER
    1.21 +
    1.22 +namespace {
    1.23 +
    1.24 +const uint32_t kBatchSize = 256;
    1.25 +const char kTypeChars[eventtracer::eLast] = {' ','N','S','W','E','D'};
    1.26 +
    1.27 +// Flushing thread and records queue monitor
    1.28 +mozilla::Monitor * gMonitor = nullptr;
    1.29 +
    1.30 +// gInitialized and gCapture can be accessed from multiple threads
    1.31 +// simultaneously without any locking.  However, since they are only ever
    1.32 +// *set* from the main thread, the chance of races manifesting is small
    1.33 +// and unlikely to be a problem in practice.
    1.34 +bool gInitialized;
    1.35 +
    1.36 +// Flag to allow capturing
    1.37 +bool gCapture;
    1.38 +
    1.39 +// Time stamp of the epoch we have started to capture
    1.40 +mozilla::TimeStamp * gProfilerStart;
    1.41 +
    1.42 +// Duration of the log to keep up to
    1.43 +mozilla::TimeDuration * gMaxBacklogTime;
    1.44 +
    1.45 +
    1.46 +// Record of a single event
    1.47 +class Record {
    1.48 +public:
    1.49 +  Record() 
    1.50 +    : mType(::mozilla::eventtracer::eNone)
    1.51 +    , mItem(nullptr)
    1.52 +    , mText(nullptr)
    1.53 +    , mText2(nullptr) 
    1.54 +  {
    1.55 +    MOZ_COUNT_CTOR(Record);
    1.56 +  } 
    1.57 +
    1.58 +  Record& operator=(const Record & aOther)
    1.59 +  {
    1.60 +    mType = aOther.mType;
    1.61 +    mTime = aOther.mTime;
    1.62 +    mItem = aOther.mItem;
    1.63 +    mText = PL_strdup(aOther.mText);
    1.64 +    mText2 = aOther.mText2 ? PL_strdup(aOther.mText2) : nullptr;
    1.65 +    return *this;
    1.66 +  }
    1.67 +
    1.68 +  ~Record() 
    1.69 +  {
    1.70 +    PL_strfree(mText2);
    1.71 +    PL_strfree(mText); 
    1.72 +    MOZ_COUNT_DTOR(Record);
    1.73 +  }
    1.74 +
    1.75 +  uint32_t mType;
    1.76 +  TimeStamp mTime;
    1.77 +  void * mItem;
    1.78 +  char * mText;
    1.79 +  char * mText2;
    1.80 +};
    1.81 +
    1.82 +char * DupCurrentThreadName()
    1.83 +{
    1.84 +  if (NS_IsMainThread())
    1.85 +    return PL_strdup("Main Thread");
    1.86 +
    1.87 +  PRThread * currentThread = PR_GetCurrentThread();
    1.88 +  const char * name = PR_GetThreadName(currentThread);
    1.89 +  if (name)
    1.90 +    return PL_strdup(name);
    1.91 +
    1.92 +  char buffer[128];
    1.93 +  PR_snprintf(buffer, 127, "Nameless %p", currentThread);
    1.94 +
    1.95 +  return PL_strdup(buffer);
    1.96 +}
    1.97 +
    1.98 +// An array of events, each thread keeps its own private instance
    1.99 +class RecordBatch {
   1.100 +public:
   1.101 +  RecordBatch(size_t aLength = kBatchSize,
   1.102 +              char * aThreadName = DupCurrentThreadName())
   1.103 +    : mRecordsHead(new Record[aLength])
   1.104 +    , mRecordsTail(mRecordsHead + aLength)
   1.105 +    , mNextRecord(mRecordsHead)
   1.106 +    , mNextBatch(nullptr)
   1.107 +    , mThreadNameCopy(aThreadName)
   1.108 +    , mClosed(false)
   1.109 +  {
   1.110 +    MOZ_COUNT_CTOR(RecordBatch);
   1.111 +  }
   1.112 +
   1.113 +  ~RecordBatch()
   1.114 +  {
   1.115 +    delete [] mRecordsHead;
   1.116 +    PL_strfree(mThreadNameCopy);
   1.117 +    MOZ_COUNT_DTOR(RecordBatch);
   1.118 +  }
   1.119 +
   1.120 +  void Close() { mClosed = true; }
   1.121 +
   1.122 +  size_t Length() const { return mNextRecord - mRecordsHead; }
   1.123 +  bool CanBeDeleted(const TimeStamp& aUntil) const;
   1.124 +
   1.125 +  static RecordBatch * Register();
   1.126 +  static void Close(void * data); // Registered on freeing thread data
   1.127 +  static RecordBatch * Clone(RecordBatch * aLog, const TimeStamp& aSince);
   1.128 +  static void Delete(RecordBatch * aLog);
   1.129 +
   1.130 +  static RecordBatch * CloneLog();
   1.131 +  static void GCLog(const TimeStamp& aUntil);
   1.132 +  static void DeleteLog();
   1.133 +
   1.134 +  Record * mRecordsHead;
   1.135 +  Record * mRecordsTail;
   1.136 +  Record * mNextRecord;
   1.137 +
   1.138 +  RecordBatch * mNextBatch;
   1.139 +  char * mThreadNameCopy;
   1.140 +  bool mClosed;
   1.141 +};
   1.142 +
   1.143 +// Protected by gMonitor, accessed concurently
   1.144 +// Linked list of batches threads want to flush on disk
   1.145 +RecordBatch * gLogHead = nullptr;
   1.146 +RecordBatch * gLogTail = nullptr;
   1.147 +
   1.148 +// Registers the batch in the linked list
   1.149 +// static
   1.150 +RecordBatch *
   1.151 +RecordBatch::Register()
   1.152 +{
   1.153 +  MonitorAutoLock mon(*gMonitor);
   1.154 +
   1.155 +  if (!gInitialized)
   1.156 +    return nullptr;
   1.157 +
   1.158 +  if (gLogHead)
   1.159 +    RecordBatch::GCLog(TimeStamp::Now() - *gMaxBacklogTime);
   1.160 +
   1.161 +  RecordBatch * batch = new RecordBatch();
   1.162 +  if (!gLogHead)
   1.163 +    gLogHead = batch;
   1.164 +  else // gLogTail is non-null
   1.165 +    gLogTail->mNextBatch = batch;
   1.166 +  gLogTail = batch;
   1.167 +
   1.168 +  mon.Notify();
   1.169 +  return batch;
   1.170 +}
   1.171 +
   1.172 +void
   1.173 +RecordBatch::Close(void * data)
   1.174 +{
   1.175 +  RecordBatch * batch = static_cast<RecordBatch*>(data);
   1.176 +  batch->Close();
   1.177 +}
   1.178 +
   1.179 +// static
   1.180 +RecordBatch *
   1.181 +RecordBatch::Clone(RecordBatch * aOther, const TimeStamp& aSince)
   1.182 +{
   1.183 +  if (!aOther)
   1.184 +    return nullptr;
   1.185 +
   1.186 +  size_t length = aOther->Length();
   1.187 +  size_t min = 0;
   1.188 +  size_t max = length;
   1.189 +  Record * record = nullptr;
   1.190 +
   1.191 +  // Binary search for record with time >= aSince
   1.192 +  size_t i;
   1.193 +  while (min < max) {
   1.194 +    i = (max + min) / 2;
   1.195 +
   1.196 +    record = aOther->mRecordsHead + i;
   1.197 +    if (record->mTime >= aSince)
   1.198 +      max = i;
   1.199 +    else
   1.200 +      min = i+1;
   1.201 +  }
   1.202 +  i = (max + min) / 2;
   1.203 +
   1.204 +  // How many Record's to copy?
   1.205 +  size_t toCopy = length - i;
   1.206 +  if (!toCopy)
   1.207 +    return RecordBatch::Clone(aOther->mNextBatch, aSince);
   1.208 +
   1.209 +  // Clone
   1.210 +  RecordBatch * clone = new RecordBatch(toCopy, PL_strdup(aOther->mThreadNameCopy));
   1.211 +  for (; i < length; ++i) {
   1.212 +    record = aOther->mRecordsHead + i;
   1.213 +    *clone->mNextRecord = *record;
   1.214 +    ++clone->mNextRecord;
   1.215 +  }
   1.216 +  clone->mNextBatch = RecordBatch::Clone(aOther->mNextBatch, aSince);
   1.217 +
   1.218 +  return clone;
   1.219 +}
   1.220 +
   1.221 +// static
   1.222 +void
   1.223 +RecordBatch::Delete(RecordBatch * aLog)
   1.224 +{
   1.225 +  while (aLog) {
   1.226 +    RecordBatch * batch = aLog;
   1.227 +    aLog = aLog->mNextBatch;
   1.228 +    delete batch;
   1.229 +  }
   1.230 +}
   1.231 +
   1.232 +// static
   1.233 +RecordBatch *
   1.234 +RecordBatch::CloneLog()
   1.235 +{
   1.236 +  TimeStamp startEpoch = *gProfilerStart;
   1.237 +  TimeStamp backlogEpoch = TimeStamp::Now() - *gMaxBacklogTime;
   1.238 +
   1.239 +  TimeStamp since = (startEpoch > backlogEpoch) ? startEpoch : backlogEpoch;
   1.240 +
   1.241 +  MonitorAutoLock mon(*gMonitor);
   1.242 +
   1.243 +  return RecordBatch::Clone(gLogHead, since);
   1.244 +}
   1.245 +
   1.246 +// static
   1.247 +void
   1.248 +RecordBatch::GCLog(const TimeStamp& aUntil)
   1.249 +{
   1.250 +  // Garbage collect all unreferenced and old batches
   1.251 +  gMonitor->AssertCurrentThreadOwns();
   1.252 +
   1.253 +  RecordBatch *volatile * referer = &gLogHead;
   1.254 +  gLogTail = nullptr;
   1.255 +
   1.256 +  RecordBatch * batch = *referer;
   1.257 +  while (batch) {
   1.258 +    if (batch->CanBeDeleted(aUntil)) {
   1.259 +      // The batch is completed and thus unreferenced by the thread
   1.260 +      // and the most recent record has time older then the time
   1.261 +      // we want to save records for, hence delete it.
   1.262 +      *referer = batch->mNextBatch;
   1.263 +      delete batch;
   1.264 +      batch = *referer;
   1.265 +    }
   1.266 +    else {
   1.267 +      // We walk the whole list, so this will end up filled with
   1.268 +      // the very last valid element of it.
   1.269 +      gLogTail = batch;
   1.270 +      // The current batch is active, examine the next in the list.
   1.271 +      batch = batch->mNextBatch;
   1.272 +      // When the next batch is found expired, we must extract it
   1.273 +      // from the list, shift the referer.
   1.274 +      referer = &((*referer)->mNextBatch);
   1.275 +    }
   1.276 +  }
   1.277 +}
   1.278 +
   1.279 +// static
   1.280 +void
   1.281 +RecordBatch::DeleteLog()
   1.282 +{
   1.283 +  RecordBatch * batch;
   1.284 +  {
   1.285 +    MonitorAutoLock mon(*gMonitor);
   1.286 +    batch = gLogHead;
   1.287 +    gLogHead = nullptr;
   1.288 +    gLogTail = nullptr;
   1.289 +  }
   1.290 +
   1.291 +  RecordBatch::Delete(batch);
   1.292 +}
   1.293 +
   1.294 +bool
   1.295 +RecordBatch::CanBeDeleted(const TimeStamp& aUntil) const
   1.296 +{
   1.297 +  if (mClosed) {
   1.298 +    // This flag is set when a thread releases this batch as
   1.299 +    // its private data.  It happens when the list is full or
   1.300 +    // when the thread ends its job.  We must not delete this
   1.301 +    // batch from memory while it's held by a thread.
   1.302 +
   1.303 +    if (!Length()) {
   1.304 +      // There are no records, just get rid of this empty batch.
   1.305 +      return true;
   1.306 +    }
   1.307 +
   1.308 +    if ((mNextRecord-1)->mTime <= aUntil) {
   1.309 +      // Is the last record older then the time we demand records
   1.310 +      // for?  If not, this batch has expired.
   1.311 +      return true;
   1.312 +    }
   1.313 +  }
   1.314 +
   1.315 +  // Not all conditions to close the batch met, keep it.
   1.316 +  return false;
   1.317 +}
   1.318 +
   1.319 +// Helper class for filtering events by MOZ_PROFILING_EVENTS
   1.320 +class EventFilter
   1.321 +{
   1.322 +public:
   1.323 +  static EventFilter * Build(const char * filterVar);
   1.324 +  bool EventPasses(const char * eventName);
   1.325 +
   1.326 +  ~EventFilter()
   1.327 +  {
   1.328 +    delete mNext;
   1.329 +    PL_strfree(mFilter);
   1.330 +    MOZ_COUNT_DTOR(EventFilter);
   1.331 +  }
   1.332 +
   1.333 +private:
   1.334 +  EventFilter(const char * eventName, EventFilter * next)
   1.335 +    : mFilter(PL_strdup(eventName))
   1.336 +    , mNext(next)
   1.337 +  {
   1.338 +    MOZ_COUNT_CTOR(EventFilter);
   1.339 +  }
   1.340 +
   1.341 +  char * mFilter;
   1.342 +  EventFilter * mNext;
   1.343 +};
   1.344 +
   1.345 +// static
   1.346 +EventFilter *
   1.347 +EventFilter::Build(const char * filterVar)
   1.348 +{
   1.349 +  if (!filterVar || !*filterVar)
   1.350 +    return nullptr;
   1.351 +
   1.352 +  // Reads a comma serpatated list of events.
   1.353 +
   1.354 +  // Copied from nspr logging code (read of NSPR_LOG_MODULES)
   1.355 +  char eventName[64];
   1.356 +  int pos = 0, count, delta = 0;
   1.357 +
   1.358 +  // Read up to a comma or EOF -> get name of an event first in the list
   1.359 +  count = sscanf(filterVar, "%63[^,]%n", eventName, &delta);
   1.360 +  if (count == 0) 
   1.361 +    return nullptr;
   1.362 +
   1.363 +  pos = delta;
   1.364 +
   1.365 +  // Skip a comma, if present, accept spaces around it
   1.366 +  count = sscanf(filterVar + pos, " , %n", &delta);
   1.367 +  if (count != EOF)
   1.368 +    pos += delta;
   1.369 +
   1.370 +  // eventName contains name of the first event in the list
   1.371 +  // second argument recursively parses the rest of the list string and
   1.372 +  // fills mNext of the just created EventFilter object chaining the objects
   1.373 +  return new EventFilter(eventName, Build(filterVar + pos));
   1.374 +}
   1.375 +
   1.376 +bool
   1.377 +EventFilter::EventPasses(const char * eventName)
   1.378 +{
   1.379 +  if (!strcmp(eventName, mFilter))
   1.380 +    return true;
   1.381 +
   1.382 +  if (mNext)
   1.383 +    return mNext->EventPasses(eventName);
   1.384 +
   1.385 +  return false;
   1.386 +}
   1.387 +
   1.388 +// State and control variables, initialized in Init() method, after it 
   1.389 +// immutable and read concurently.
   1.390 +EventFilter * gEventFilter = nullptr;
   1.391 +unsigned gThreadPrivateIndex;
   1.392 +
   1.393 +// static
   1.394 +bool CheckEventFilters(uint32_t aType, void * aItem, const char * aText)
   1.395 +{
   1.396 +  if (!gEventFilter)
   1.397 +    return true;
   1.398 +
   1.399 +  if (aType == eName)
   1.400 +    return true;
   1.401 +
   1.402 +  return gEventFilter->EventPasses(aText);
   1.403 +}
   1.404 +
   1.405 +} // anon namespace
   1.406 +
   1.407 +#endif //MOZ_VISUAL_EVENT_TRACER
   1.408 +
   1.409 +// static 
   1.410 +void Init()
   1.411 +{
   1.412 +#ifdef MOZ_VISUAL_EVENT_TRACER
   1.413 +  const char * logEvents = PR_GetEnv("MOZ_PROFILING_EVENTS");
   1.414 +  if (logEvents && *logEvents)
   1.415 +    gEventFilter = EventFilter::Build(logEvents);
   1.416 +
   1.417 +  PRStatus status = PR_NewThreadPrivateIndex(&gThreadPrivateIndex, &RecordBatch::Close);
   1.418 +  if (status != PR_SUCCESS)
   1.419 +    return;
   1.420 +
   1.421 +  gMonitor = new mozilla::Monitor("Profiler");
   1.422 +  if (!gMonitor)
   1.423 +    return;
   1.424 +
   1.425 +  gProfilerStart = new mozilla::TimeStamp();
   1.426 +  gMaxBacklogTime = new mozilla::TimeDuration();
   1.427 +
   1.428 +  gInitialized = true;
   1.429 +#endif
   1.430 +}
   1.431 +
   1.432 +// static 
   1.433 +void Shutdown()
   1.434 +{
   1.435 +#ifdef MOZ_VISUAL_EVENT_TRACER
   1.436 +  gCapture = false;
   1.437 +  gInitialized = false;
   1.438 +
   1.439 +  RecordBatch::DeleteLog();
   1.440 +
   1.441 +  if (gMonitor) {
   1.442 +    delete gMonitor;
   1.443 +    gMonitor = nullptr;
   1.444 +  }
   1.445 +
   1.446 +  if (gEventFilter) {
   1.447 +    delete gEventFilter;
   1.448 +    gEventFilter = nullptr;
   1.449 +  }
   1.450 +
   1.451 +  if (gProfilerStart) {
   1.452 +    delete gProfilerStart;
   1.453 +    gProfilerStart = nullptr;
   1.454 +  }
   1.455 +
   1.456 +  if (gMaxBacklogTime) {
   1.457 +    delete gMaxBacklogTime;
   1.458 +    gMaxBacklogTime = nullptr;
   1.459 +  }
   1.460 +#endif
   1.461 +}
   1.462 +
   1.463 +// static 
   1.464 +void Mark(uint32_t aType, void * aItem, const char * aText, const char * aText2)
   1.465 +{
   1.466 +#ifdef MOZ_VISUAL_EVENT_TRACER
   1.467 +  if (!gInitialized || !gCapture)
   1.468 +    return;
   1.469 +
   1.470 +  if (aType == eNone)
   1.471 +    return;
   1.472 +
   1.473 +  if (!CheckEventFilters(aType, aItem, aText)) // Events use just aText
   1.474 +    return;
   1.475 +
   1.476 +  RecordBatch * threadLogPrivate = static_cast<RecordBatch *>(
   1.477 +      PR_GetThreadPrivate(gThreadPrivateIndex));
   1.478 +  if (!threadLogPrivate) {
   1.479 +    threadLogPrivate = RecordBatch::Register();
   1.480 +    if (!threadLogPrivate)
   1.481 +      return;
   1.482 +
   1.483 +    PR_SetThreadPrivate(gThreadPrivateIndex, threadLogPrivate);
   1.484 +  }
   1.485 +
   1.486 +  Record * record = threadLogPrivate->mNextRecord;
   1.487 +  record->mType = aType;
   1.488 +  record->mTime = mozilla::TimeStamp::Now();
   1.489 +  record->mItem = aItem;
   1.490 +  record->mText = PL_strdup(aText);
   1.491 +  record->mText2 = aText2 ? PL_strdup(aText2) : nullptr;
   1.492 +
   1.493 +  ++threadLogPrivate->mNextRecord;
   1.494 +  if (threadLogPrivate->mNextRecord == threadLogPrivate->mRecordsTail) {
   1.495 +    // Calls RecordBatch::Close(threadLogPrivate) that marks
   1.496 +    // the batch as OK to be deleted from memory when no longer needed.
   1.497 +    PR_SetThreadPrivate(gThreadPrivateIndex, nullptr);
   1.498 +  }
   1.499 +#endif
   1.500 +}
   1.501 +
   1.502 +
   1.503 +#ifdef MOZ_VISUAL_EVENT_TRACER
   1.504 +
   1.505 +// The scriptable classes
   1.506 +
   1.507 +class VisualEventTracerLog : public nsIVisualEventTracerLog
   1.508 +{
   1.509 +  NS_DECL_ISUPPORTS
   1.510 +  NS_DECL_NSIVISUALEVENTTRACERLOG
   1.511 +
   1.512 +  VisualEventTracerLog(RecordBatch* aBatch)
   1.513 +    : mBatch(aBatch)
   1.514 +    , mProfilerStart(*gProfilerStart)
   1.515 +  {}
   1.516 +
   1.517 +  virtual ~VisualEventTracerLog();
   1.518 +
   1.519 +protected:
   1.520 +  RecordBatch * mBatch;
   1.521 +  TimeStamp mProfilerStart;
   1.522 +};
   1.523 +
   1.524 +NS_IMPL_ISUPPORTS(VisualEventTracerLog, nsIVisualEventTracerLog)
   1.525 +
   1.526 +VisualEventTracerLog::~VisualEventTracerLog()
   1.527 +{
   1.528 +  RecordBatch::Delete(mBatch);
   1.529 +}
   1.530 +
   1.531 +NS_IMETHODIMP
   1.532 +VisualEventTracerLog::GetJSONString(nsACString & _retval)
   1.533 +{
   1.534 +  nsCString buffer;
   1.535 +
   1.536 +  buffer.Assign(NS_LITERAL_CSTRING("{\n\"version\": 1,\n\"records\":[\n"));
   1.537 +
   1.538 +  RecordBatch * batch = mBatch;
   1.539 +  while (batch) {
   1.540 +    if (batch != mBatch) {
   1.541 +      // This is not the first batch we are writting, add comma
   1.542 +      buffer.Append(NS_LITERAL_CSTRING(",\n"));
   1.543 +    }
   1.544 +
   1.545 +    buffer.Append(NS_LITERAL_CSTRING("{\"thread\":\""));
   1.546 +    buffer.Append(batch->mThreadNameCopy);
   1.547 +    buffer.Append(NS_LITERAL_CSTRING("\",\"log\":[\n"));
   1.548 +
   1.549 +    static const int kBufferSize = 2048;
   1.550 +    char buf[kBufferSize];
   1.551 +
   1.552 +    for (Record * record = batch->mRecordsHead;
   1.553 +         record < batch->mNextRecord;
   1.554 +         ++record) {
   1.555 +
   1.556 +      // mType carries both type and flags, separate type
   1.557 +      // as lower 16 bits and flags as higher 16 bits.
   1.558 +      // The json format expects this separated.
   1.559 +      uint32_t type = record->mType & 0xffffUL;
   1.560 +      uint32_t flags = record->mType >> 16;
   1.561 +      PR_snprintf(buf, kBufferSize,
   1.562 +        "{\"e\":\"%c\",\"t\":%llu,\"f\":%d,\"i\":\"%p\",\"n\":\"%s%s\"}%s\n",
   1.563 +        kTypeChars[type],
   1.564 +        static_cast<uint64_t>((record->mTime - mProfilerStart).ToMilliseconds()),
   1.565 +        flags,
   1.566 +        record->mItem,
   1.567 +        record->mText,
   1.568 +        record->mText2 ? record->mText2 : "",
   1.569 +        (record == batch->mNextRecord - 1) ? "" : ",");
   1.570 +
   1.571 +      buffer.Append(buf);
   1.572 +    }
   1.573 +
   1.574 +    buffer.Append(NS_LITERAL_CSTRING("]}\n"));
   1.575 +
   1.576 +    RecordBatch * next = batch->mNextBatch;
   1.577 +    batch = next;
   1.578 +  }
   1.579 +
   1.580 +  buffer.Append(NS_LITERAL_CSTRING("]}\n"));
   1.581 +  _retval.Assign(buffer);
   1.582 +
   1.583 +  return NS_OK;
   1.584 +}
   1.585 +
   1.586 +nsresult
   1.587 +VisualEventTracerLog::WriteToProfilingFile()
   1.588 +{
   1.589 +  const char* filename = PR_GetEnv("MOZ_TRACE_FILE");
   1.590 +  if (!filename) {
   1.591 +    return NS_OK;
   1.592 +  }
   1.593 +
   1.594 +  PRFileDesc* fd = PR_Open(filename, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
   1.595 +			   0644);
   1.596 +  if (!fd) {
   1.597 +    return NS_ERROR_FILE_ACCESS_DENIED;
   1.598 +  }
   1.599 +
   1.600 +  nsCString json;
   1.601 +  GetJSONString(json);
   1.602 +
   1.603 +  int32_t bytesWritten = PR_Write(fd, json.get(), json.Length());
   1.604 +  PR_Close(fd);
   1.605 +
   1.606 +  if (bytesWritten < json.Length()) {
   1.607 +    return NS_ERROR_UNEXPECTED;
   1.608 +  }
   1.609 +
   1.610 +  return NS_OK;
   1.611 +}
   1.612 +
   1.613 +NS_IMPL_ISUPPORTS(VisualEventTracer, nsIVisualEventTracer)
   1.614 +
   1.615 +NS_IMETHODIMP
   1.616 +VisualEventTracer::Start(const uint32_t aMaxBacklogSeconds)
   1.617 +{
   1.618 +  if (!gInitialized)
   1.619 +    return NS_ERROR_UNEXPECTED;
   1.620 +
   1.621 +  if (gCapture) {
   1.622 +    NS_WARNING("VisualEventTracer has already been started");
   1.623 +    return NS_ERROR_ALREADY_INITIALIZED;
   1.624 +  }
   1.625 +
   1.626 +  *gMaxBacklogTime = TimeDuration::FromMilliseconds(aMaxBacklogSeconds * 1000);
   1.627 +
   1.628 +  *gProfilerStart = mozilla::TimeStamp::Now();
   1.629 +  {
   1.630 +    MonitorAutoLock mon(*gMonitor);
   1.631 +    RecordBatch::GCLog(*gProfilerStart);
   1.632 +  }
   1.633 +  gCapture = true;
   1.634 +
   1.635 +  MOZ_EVENT_TRACER_MARK(this, "trace::start");
   1.636 +
   1.637 +  return NS_OK;
   1.638 +}
   1.639 +
   1.640 +NS_IMETHODIMP
   1.641 +VisualEventTracer::Stop()
   1.642 +{
   1.643 +  if (!gInitialized)
   1.644 +    return NS_ERROR_UNEXPECTED;
   1.645 +
   1.646 +  if (!gCapture) {
   1.647 +    NS_WARNING("VisualEventTracer is not runing");
   1.648 +    return NS_ERROR_NOT_INITIALIZED;
   1.649 +  }
   1.650 +
   1.651 +  MOZ_EVENT_TRACER_MARK(this, "trace::stop");
   1.652 +
   1.653 +  gCapture = false;
   1.654 +
   1.655 +  nsresult rv = NS_OK;
   1.656 +  if (PR_GetEnv("MOZ_TRACE_FILE")) {
   1.657 +    nsCOMPtr<nsIVisualEventTracerLog> tracelog;
   1.658 +    rv = Snapshot(getter_AddRefs(tracelog));
   1.659 +    if (NS_SUCCEEDED(rv)) {
   1.660 +      rv = tracelog->WriteToProfilingFile();
   1.661 +    }
   1.662 +  }
   1.663 +
   1.664 +  return rv;
   1.665 +}
   1.666 +
   1.667 +NS_IMETHODIMP
   1.668 +VisualEventTracer::Snapshot(nsIVisualEventTracerLog ** _result)
   1.669 +{
   1.670 +  if (!gInitialized)
   1.671 +    return NS_ERROR_UNEXPECTED;
   1.672 +
   1.673 +  RecordBatch * batch = RecordBatch::CloneLog();
   1.674 +
   1.675 +  nsRefPtr<VisualEventTracerLog> log = new VisualEventTracerLog(batch);
   1.676 +  log.forget(_result);
   1.677 +
   1.678 +  return NS_OK;
   1.679 +}
   1.680 +
   1.681 +#endif
   1.682 +
   1.683 +} // eventtracer
   1.684 +} // mozilla

mercurial