xpcom/base/VisualEventTracer.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 #include "mozilla/VisualEventTracer.h"
     6 #include "mozilla/Monitor.h"
     7 #include "mozilla/TimeStamp.h"
     8 #include "nscore.h"
     9 #include "prthread.h"
    10 #include "prprf.h"
    11 #include "prenv.h"
    12 #include "plstr.h"
    13 #include "nsThreadUtils.h"
    15 namespace mozilla { namespace eventtracer {
    17 #ifdef MOZ_VISUAL_EVENT_TRACER
    19 namespace {
    21 const uint32_t kBatchSize = 256;
    22 const char kTypeChars[eventtracer::eLast] = {' ','N','S','W','E','D'};
    24 // Flushing thread and records queue monitor
    25 mozilla::Monitor * gMonitor = nullptr;
    27 // gInitialized and gCapture can be accessed from multiple threads
    28 // simultaneously without any locking.  However, since they are only ever
    29 // *set* from the main thread, the chance of races manifesting is small
    30 // and unlikely to be a problem in practice.
    31 bool gInitialized;
    33 // Flag to allow capturing
    34 bool gCapture;
    36 // Time stamp of the epoch we have started to capture
    37 mozilla::TimeStamp * gProfilerStart;
    39 // Duration of the log to keep up to
    40 mozilla::TimeDuration * gMaxBacklogTime;
    43 // Record of a single event
    44 class Record {
    45 public:
    46   Record() 
    47     : mType(::mozilla::eventtracer::eNone)
    48     , mItem(nullptr)
    49     , mText(nullptr)
    50     , mText2(nullptr) 
    51   {
    52     MOZ_COUNT_CTOR(Record);
    53   } 
    55   Record& operator=(const Record & aOther)
    56   {
    57     mType = aOther.mType;
    58     mTime = aOther.mTime;
    59     mItem = aOther.mItem;
    60     mText = PL_strdup(aOther.mText);
    61     mText2 = aOther.mText2 ? PL_strdup(aOther.mText2) : nullptr;
    62     return *this;
    63   }
    65   ~Record() 
    66   {
    67     PL_strfree(mText2);
    68     PL_strfree(mText); 
    69     MOZ_COUNT_DTOR(Record);
    70   }
    72   uint32_t mType;
    73   TimeStamp mTime;
    74   void * mItem;
    75   char * mText;
    76   char * mText2;
    77 };
    79 char * DupCurrentThreadName()
    80 {
    81   if (NS_IsMainThread())
    82     return PL_strdup("Main Thread");
    84   PRThread * currentThread = PR_GetCurrentThread();
    85   const char * name = PR_GetThreadName(currentThread);
    86   if (name)
    87     return PL_strdup(name);
    89   char buffer[128];
    90   PR_snprintf(buffer, 127, "Nameless %p", currentThread);
    92   return PL_strdup(buffer);
    93 }
    95 // An array of events, each thread keeps its own private instance
    96 class RecordBatch {
    97 public:
    98   RecordBatch(size_t aLength = kBatchSize,
    99               char * aThreadName = DupCurrentThreadName())
   100     : mRecordsHead(new Record[aLength])
   101     , mRecordsTail(mRecordsHead + aLength)
   102     , mNextRecord(mRecordsHead)
   103     , mNextBatch(nullptr)
   104     , mThreadNameCopy(aThreadName)
   105     , mClosed(false)
   106   {
   107     MOZ_COUNT_CTOR(RecordBatch);
   108   }
   110   ~RecordBatch()
   111   {
   112     delete [] mRecordsHead;
   113     PL_strfree(mThreadNameCopy);
   114     MOZ_COUNT_DTOR(RecordBatch);
   115   }
   117   void Close() { mClosed = true; }
   119   size_t Length() const { return mNextRecord - mRecordsHead; }
   120   bool CanBeDeleted(const TimeStamp& aUntil) const;
   122   static RecordBatch * Register();
   123   static void Close(void * data); // Registered on freeing thread data
   124   static RecordBatch * Clone(RecordBatch * aLog, const TimeStamp& aSince);
   125   static void Delete(RecordBatch * aLog);
   127   static RecordBatch * CloneLog();
   128   static void GCLog(const TimeStamp& aUntil);
   129   static void DeleteLog();
   131   Record * mRecordsHead;
   132   Record * mRecordsTail;
   133   Record * mNextRecord;
   135   RecordBatch * mNextBatch;
   136   char * mThreadNameCopy;
   137   bool mClosed;
   138 };
   140 // Protected by gMonitor, accessed concurently
   141 // Linked list of batches threads want to flush on disk
   142 RecordBatch * gLogHead = nullptr;
   143 RecordBatch * gLogTail = nullptr;
   145 // Registers the batch in the linked list
   146 // static
   147 RecordBatch *
   148 RecordBatch::Register()
   149 {
   150   MonitorAutoLock mon(*gMonitor);
   152   if (!gInitialized)
   153     return nullptr;
   155   if (gLogHead)
   156     RecordBatch::GCLog(TimeStamp::Now() - *gMaxBacklogTime);
   158   RecordBatch * batch = new RecordBatch();
   159   if (!gLogHead)
   160     gLogHead = batch;
   161   else // gLogTail is non-null
   162     gLogTail->mNextBatch = batch;
   163   gLogTail = batch;
   165   mon.Notify();
   166   return batch;
   167 }
   169 void
   170 RecordBatch::Close(void * data)
   171 {
   172   RecordBatch * batch = static_cast<RecordBatch*>(data);
   173   batch->Close();
   174 }
   176 // static
   177 RecordBatch *
   178 RecordBatch::Clone(RecordBatch * aOther, const TimeStamp& aSince)
   179 {
   180   if (!aOther)
   181     return nullptr;
   183   size_t length = aOther->Length();
   184   size_t min = 0;
   185   size_t max = length;
   186   Record * record = nullptr;
   188   // Binary search for record with time >= aSince
   189   size_t i;
   190   while (min < max) {
   191     i = (max + min) / 2;
   193     record = aOther->mRecordsHead + i;
   194     if (record->mTime >= aSince)
   195       max = i;
   196     else
   197       min = i+1;
   198   }
   199   i = (max + min) / 2;
   201   // How many Record's to copy?
   202   size_t toCopy = length - i;
   203   if (!toCopy)
   204     return RecordBatch::Clone(aOther->mNextBatch, aSince);
   206   // Clone
   207   RecordBatch * clone = new RecordBatch(toCopy, PL_strdup(aOther->mThreadNameCopy));
   208   for (; i < length; ++i) {
   209     record = aOther->mRecordsHead + i;
   210     *clone->mNextRecord = *record;
   211     ++clone->mNextRecord;
   212   }
   213   clone->mNextBatch = RecordBatch::Clone(aOther->mNextBatch, aSince);
   215   return clone;
   216 }
   218 // static
   219 void
   220 RecordBatch::Delete(RecordBatch * aLog)
   221 {
   222   while (aLog) {
   223     RecordBatch * batch = aLog;
   224     aLog = aLog->mNextBatch;
   225     delete batch;
   226   }
   227 }
   229 // static
   230 RecordBatch *
   231 RecordBatch::CloneLog()
   232 {
   233   TimeStamp startEpoch = *gProfilerStart;
   234   TimeStamp backlogEpoch = TimeStamp::Now() - *gMaxBacklogTime;
   236   TimeStamp since = (startEpoch > backlogEpoch) ? startEpoch : backlogEpoch;
   238   MonitorAutoLock mon(*gMonitor);
   240   return RecordBatch::Clone(gLogHead, since);
   241 }
   243 // static
   244 void
   245 RecordBatch::GCLog(const TimeStamp& aUntil)
   246 {
   247   // Garbage collect all unreferenced and old batches
   248   gMonitor->AssertCurrentThreadOwns();
   250   RecordBatch *volatile * referer = &gLogHead;
   251   gLogTail = nullptr;
   253   RecordBatch * batch = *referer;
   254   while (batch) {
   255     if (batch->CanBeDeleted(aUntil)) {
   256       // The batch is completed and thus unreferenced by the thread
   257       // and the most recent record has time older then the time
   258       // we want to save records for, hence delete it.
   259       *referer = batch->mNextBatch;
   260       delete batch;
   261       batch = *referer;
   262     }
   263     else {
   264       // We walk the whole list, so this will end up filled with
   265       // the very last valid element of it.
   266       gLogTail = batch;
   267       // The current batch is active, examine the next in the list.
   268       batch = batch->mNextBatch;
   269       // When the next batch is found expired, we must extract it
   270       // from the list, shift the referer.
   271       referer = &((*referer)->mNextBatch);
   272     }
   273   }
   274 }
   276 // static
   277 void
   278 RecordBatch::DeleteLog()
   279 {
   280   RecordBatch * batch;
   281   {
   282     MonitorAutoLock mon(*gMonitor);
   283     batch = gLogHead;
   284     gLogHead = nullptr;
   285     gLogTail = nullptr;
   286   }
   288   RecordBatch::Delete(batch);
   289 }
   291 bool
   292 RecordBatch::CanBeDeleted(const TimeStamp& aUntil) const
   293 {
   294   if (mClosed) {
   295     // This flag is set when a thread releases this batch as
   296     // its private data.  It happens when the list is full or
   297     // when the thread ends its job.  We must not delete this
   298     // batch from memory while it's held by a thread.
   300     if (!Length()) {
   301       // There are no records, just get rid of this empty batch.
   302       return true;
   303     }
   305     if ((mNextRecord-1)->mTime <= aUntil) {
   306       // Is the last record older then the time we demand records
   307       // for?  If not, this batch has expired.
   308       return true;
   309     }
   310   }
   312   // Not all conditions to close the batch met, keep it.
   313   return false;
   314 }
   316 // Helper class for filtering events by MOZ_PROFILING_EVENTS
   317 class EventFilter
   318 {
   319 public:
   320   static EventFilter * Build(const char * filterVar);
   321   bool EventPasses(const char * eventName);
   323   ~EventFilter()
   324   {
   325     delete mNext;
   326     PL_strfree(mFilter);
   327     MOZ_COUNT_DTOR(EventFilter);
   328   }
   330 private:
   331   EventFilter(const char * eventName, EventFilter * next)
   332     : mFilter(PL_strdup(eventName))
   333     , mNext(next)
   334   {
   335     MOZ_COUNT_CTOR(EventFilter);
   336   }
   338   char * mFilter;
   339   EventFilter * mNext;
   340 };
   342 // static
   343 EventFilter *
   344 EventFilter::Build(const char * filterVar)
   345 {
   346   if (!filterVar || !*filterVar)
   347     return nullptr;
   349   // Reads a comma serpatated list of events.
   351   // Copied from nspr logging code (read of NSPR_LOG_MODULES)
   352   char eventName[64];
   353   int pos = 0, count, delta = 0;
   355   // Read up to a comma or EOF -> get name of an event first in the list
   356   count = sscanf(filterVar, "%63[^,]%n", eventName, &delta);
   357   if (count == 0) 
   358     return nullptr;
   360   pos = delta;
   362   // Skip a comma, if present, accept spaces around it
   363   count = sscanf(filterVar + pos, " , %n", &delta);
   364   if (count != EOF)
   365     pos += delta;
   367   // eventName contains name of the first event in the list
   368   // second argument recursively parses the rest of the list string and
   369   // fills mNext of the just created EventFilter object chaining the objects
   370   return new EventFilter(eventName, Build(filterVar + pos));
   371 }
   373 bool
   374 EventFilter::EventPasses(const char * eventName)
   375 {
   376   if (!strcmp(eventName, mFilter))
   377     return true;
   379   if (mNext)
   380     return mNext->EventPasses(eventName);
   382   return false;
   383 }
   385 // State and control variables, initialized in Init() method, after it 
   386 // immutable and read concurently.
   387 EventFilter * gEventFilter = nullptr;
   388 unsigned gThreadPrivateIndex;
   390 // static
   391 bool CheckEventFilters(uint32_t aType, void * aItem, const char * aText)
   392 {
   393   if (!gEventFilter)
   394     return true;
   396   if (aType == eName)
   397     return true;
   399   return gEventFilter->EventPasses(aText);
   400 }
   402 } // anon namespace
   404 #endif //MOZ_VISUAL_EVENT_TRACER
   406 // static 
   407 void Init()
   408 {
   409 #ifdef MOZ_VISUAL_EVENT_TRACER
   410   const char * logEvents = PR_GetEnv("MOZ_PROFILING_EVENTS");
   411   if (logEvents && *logEvents)
   412     gEventFilter = EventFilter::Build(logEvents);
   414   PRStatus status = PR_NewThreadPrivateIndex(&gThreadPrivateIndex, &RecordBatch::Close);
   415   if (status != PR_SUCCESS)
   416     return;
   418   gMonitor = new mozilla::Monitor("Profiler");
   419   if (!gMonitor)
   420     return;
   422   gProfilerStart = new mozilla::TimeStamp();
   423   gMaxBacklogTime = new mozilla::TimeDuration();
   425   gInitialized = true;
   426 #endif
   427 }
   429 // static 
   430 void Shutdown()
   431 {
   432 #ifdef MOZ_VISUAL_EVENT_TRACER
   433   gCapture = false;
   434   gInitialized = false;
   436   RecordBatch::DeleteLog();
   438   if (gMonitor) {
   439     delete gMonitor;
   440     gMonitor = nullptr;
   441   }
   443   if (gEventFilter) {
   444     delete gEventFilter;
   445     gEventFilter = nullptr;
   446   }
   448   if (gProfilerStart) {
   449     delete gProfilerStart;
   450     gProfilerStart = nullptr;
   451   }
   453   if (gMaxBacklogTime) {
   454     delete gMaxBacklogTime;
   455     gMaxBacklogTime = nullptr;
   456   }
   457 #endif
   458 }
   460 // static 
   461 void Mark(uint32_t aType, void * aItem, const char * aText, const char * aText2)
   462 {
   463 #ifdef MOZ_VISUAL_EVENT_TRACER
   464   if (!gInitialized || !gCapture)
   465     return;
   467   if (aType == eNone)
   468     return;
   470   if (!CheckEventFilters(aType, aItem, aText)) // Events use just aText
   471     return;
   473   RecordBatch * threadLogPrivate = static_cast<RecordBatch *>(
   474       PR_GetThreadPrivate(gThreadPrivateIndex));
   475   if (!threadLogPrivate) {
   476     threadLogPrivate = RecordBatch::Register();
   477     if (!threadLogPrivate)
   478       return;
   480     PR_SetThreadPrivate(gThreadPrivateIndex, threadLogPrivate);
   481   }
   483   Record * record = threadLogPrivate->mNextRecord;
   484   record->mType = aType;
   485   record->mTime = mozilla::TimeStamp::Now();
   486   record->mItem = aItem;
   487   record->mText = PL_strdup(aText);
   488   record->mText2 = aText2 ? PL_strdup(aText2) : nullptr;
   490   ++threadLogPrivate->mNextRecord;
   491   if (threadLogPrivate->mNextRecord == threadLogPrivate->mRecordsTail) {
   492     // Calls RecordBatch::Close(threadLogPrivate) that marks
   493     // the batch as OK to be deleted from memory when no longer needed.
   494     PR_SetThreadPrivate(gThreadPrivateIndex, nullptr);
   495   }
   496 #endif
   497 }
   500 #ifdef MOZ_VISUAL_EVENT_TRACER
   502 // The scriptable classes
   504 class VisualEventTracerLog : public nsIVisualEventTracerLog
   505 {
   506   NS_DECL_ISUPPORTS
   507   NS_DECL_NSIVISUALEVENTTRACERLOG
   509   VisualEventTracerLog(RecordBatch* aBatch)
   510     : mBatch(aBatch)
   511     , mProfilerStart(*gProfilerStart)
   512   {}
   514   virtual ~VisualEventTracerLog();
   516 protected:
   517   RecordBatch * mBatch;
   518   TimeStamp mProfilerStart;
   519 };
   521 NS_IMPL_ISUPPORTS(VisualEventTracerLog, nsIVisualEventTracerLog)
   523 VisualEventTracerLog::~VisualEventTracerLog()
   524 {
   525   RecordBatch::Delete(mBatch);
   526 }
   528 NS_IMETHODIMP
   529 VisualEventTracerLog::GetJSONString(nsACString & _retval)
   530 {
   531   nsCString buffer;
   533   buffer.Assign(NS_LITERAL_CSTRING("{\n\"version\": 1,\n\"records\":[\n"));
   535   RecordBatch * batch = mBatch;
   536   while (batch) {
   537     if (batch != mBatch) {
   538       // This is not the first batch we are writting, add comma
   539       buffer.Append(NS_LITERAL_CSTRING(",\n"));
   540     }
   542     buffer.Append(NS_LITERAL_CSTRING("{\"thread\":\""));
   543     buffer.Append(batch->mThreadNameCopy);
   544     buffer.Append(NS_LITERAL_CSTRING("\",\"log\":[\n"));
   546     static const int kBufferSize = 2048;
   547     char buf[kBufferSize];
   549     for (Record * record = batch->mRecordsHead;
   550          record < batch->mNextRecord;
   551          ++record) {
   553       // mType carries both type and flags, separate type
   554       // as lower 16 bits and flags as higher 16 bits.
   555       // The json format expects this separated.
   556       uint32_t type = record->mType & 0xffffUL;
   557       uint32_t flags = record->mType >> 16;
   558       PR_snprintf(buf, kBufferSize,
   559         "{\"e\":\"%c\",\"t\":%llu,\"f\":%d,\"i\":\"%p\",\"n\":\"%s%s\"}%s\n",
   560         kTypeChars[type],
   561         static_cast<uint64_t>((record->mTime - mProfilerStart).ToMilliseconds()),
   562         flags,
   563         record->mItem,
   564         record->mText,
   565         record->mText2 ? record->mText2 : "",
   566         (record == batch->mNextRecord - 1) ? "" : ",");
   568       buffer.Append(buf);
   569     }
   571     buffer.Append(NS_LITERAL_CSTRING("]}\n"));
   573     RecordBatch * next = batch->mNextBatch;
   574     batch = next;
   575   }
   577   buffer.Append(NS_LITERAL_CSTRING("]}\n"));
   578   _retval.Assign(buffer);
   580   return NS_OK;
   581 }
   583 nsresult
   584 VisualEventTracerLog::WriteToProfilingFile()
   585 {
   586   const char* filename = PR_GetEnv("MOZ_TRACE_FILE");
   587   if (!filename) {
   588     return NS_OK;
   589   }
   591   PRFileDesc* fd = PR_Open(filename, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
   592 			   0644);
   593   if (!fd) {
   594     return NS_ERROR_FILE_ACCESS_DENIED;
   595   }
   597   nsCString json;
   598   GetJSONString(json);
   600   int32_t bytesWritten = PR_Write(fd, json.get(), json.Length());
   601   PR_Close(fd);
   603   if (bytesWritten < json.Length()) {
   604     return NS_ERROR_UNEXPECTED;
   605   }
   607   return NS_OK;
   608 }
   610 NS_IMPL_ISUPPORTS(VisualEventTracer, nsIVisualEventTracer)
   612 NS_IMETHODIMP
   613 VisualEventTracer::Start(const uint32_t aMaxBacklogSeconds)
   614 {
   615   if (!gInitialized)
   616     return NS_ERROR_UNEXPECTED;
   618   if (gCapture) {
   619     NS_WARNING("VisualEventTracer has already been started");
   620     return NS_ERROR_ALREADY_INITIALIZED;
   621   }
   623   *gMaxBacklogTime = TimeDuration::FromMilliseconds(aMaxBacklogSeconds * 1000);
   625   *gProfilerStart = mozilla::TimeStamp::Now();
   626   {
   627     MonitorAutoLock mon(*gMonitor);
   628     RecordBatch::GCLog(*gProfilerStart);
   629   }
   630   gCapture = true;
   632   MOZ_EVENT_TRACER_MARK(this, "trace::start");
   634   return NS_OK;
   635 }
   637 NS_IMETHODIMP
   638 VisualEventTracer::Stop()
   639 {
   640   if (!gInitialized)
   641     return NS_ERROR_UNEXPECTED;
   643   if (!gCapture) {
   644     NS_WARNING("VisualEventTracer is not runing");
   645     return NS_ERROR_NOT_INITIALIZED;
   646   }
   648   MOZ_EVENT_TRACER_MARK(this, "trace::stop");
   650   gCapture = false;
   652   nsresult rv = NS_OK;
   653   if (PR_GetEnv("MOZ_TRACE_FILE")) {
   654     nsCOMPtr<nsIVisualEventTracerLog> tracelog;
   655     rv = Snapshot(getter_AddRefs(tracelog));
   656     if (NS_SUCCEEDED(rv)) {
   657       rv = tracelog->WriteToProfilingFile();
   658     }
   659   }
   661   return rv;
   662 }
   664 NS_IMETHODIMP
   665 VisualEventTracer::Snapshot(nsIVisualEventTracerLog ** _result)
   666 {
   667   if (!gInitialized)
   668     return NS_ERROR_UNEXPECTED;
   670   RecordBatch * batch = RecordBatch::CloneLog();
   672   nsRefPtr<VisualEventTracerLog> log = new VisualEventTracerLog(batch);
   673   log.forget(_result);
   675   return NS_OK;
   676 }
   678 #endif
   680 } // eventtracer
   681 } // mozilla

mercurial