LCOV - code coverage report
Current view: directory - tools/profiler - TableTicker.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 232 11 4.7 %
Date: 2012-06-02 Functions: 43 3 7.0 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is mozilla.org code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Mozilla Foundation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2011
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Benoit Girard <bgirard@mozilla.com>
      24                 :  *
      25                 :  * Alternatively, the contents of this file may be used under the terms of
      26                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      27                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      29                 :  * of those above. If you wish to allow use of your version of this file only
      30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      31                 :  * use your version of this file under the terms of the MPL, indicate your
      32                 :  * decision by deleting the provisions above and replace them with the notice
      33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      34                 :  * the provisions above, a recipient may use your version of this file under
      35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK ***** */
      38                 : 
      39                 : #include <string>
      40                 : #include <stdio.h>
      41                 : #include "sps_sampler.h"
      42                 : #include "platform.h"
      43                 : #include "nsXULAppAPI.h"
      44                 : #include "nsThreadUtils.h"
      45                 : #include "prenv.h"
      46                 : #include "shared-libraries.h"
      47                 : #include "mozilla/StringBuilder.h"
      48                 : #include "mozilla/StackWalk.h"
      49                 : #include "JSObjectBuilder.h"
      50                 : 
      51                 : // we eventually want to make this runtime switchable
      52                 : #if defined(MOZ_PROFILING) && (defined(XP_UNIX) && !defined(XP_MACOSX))
      53                 :  #ifndef ANDROID
      54                 :   #define USE_BACKTRACE
      55                 :  #endif
      56                 : #endif
      57                 : #ifdef USE_BACKTRACE
      58                 :  #include <execinfo.h>
      59                 : #endif
      60                 : 
      61                 : #if defined(MOZ_PROFILING) && (defined(XP_MACOSX) || defined(XP_WIN))
      62                 :  #define USE_NS_STACKWALK
      63                 : #endif
      64                 : #ifdef USE_NS_STACKWALK
      65                 :  #include "nsStackWalk.h"
      66                 : #endif
      67                 : 
      68                 : using std::string;
      69                 : using namespace mozilla;
      70                 : 
      71                 : #ifdef XP_WIN
      72                 :  #include <windows.h>
      73                 :  #define getpid GetCurrentProcessId
      74                 : #else
      75                 :  #include <unistd.h>
      76                 : #endif
      77                 : 
      78                 : #ifndef MAXPATHLEN
      79                 :  #ifdef PATH_MAX
      80                 :   #define MAXPATHLEN PATH_MAX
      81                 :  #elif defined(MAX_PATH)
      82                 :   #define MAXPATHLEN MAX_PATH
      83                 :  #elif defined(_MAX_PATH)
      84                 :   #define MAXPATHLEN _MAX_PATH
      85                 :  #elif defined(CCHMAXPATH)
      86                 :   #define MAXPATHLEN CCHMAXPATH
      87                 :  #else
      88                 :   #define MAXPATHLEN 1024
      89                 :  #endif
      90                 : #endif
      91                 : 
      92                 : #if _MSC_VER
      93                 :  #define snprintf _snprintf
      94                 : #endif
      95                 : 
      96                 : 
      97                 : mozilla::tls::key pkey_stack;
      98                 : mozilla::tls::key pkey_ticker;
      99                 : // We need to track whether we've been initialized otherwise
     100                 : // we end up using pkey_stack without initializing it.
     101                 : // Because pkey_stack is totally opaque to us we can't reuse
     102                 : // it as the flag itself.
     103                 : bool stack_key_initialized;
     104                 : 
     105            1464 : TimeStamp sLastTracerEvent;
     106                 : 
     107                 : class ThreadProfile;
     108                 : 
     109                 : class ProfileEntry
     110                 : {
     111                 : public:
     112               0 :   ProfileEntry()
     113                 :     : mTagData(NULL)
     114                 :     , mLeafAddress(0)
     115               0 :     , mTagName(0)
     116               0 :   { }
     117                 : 
     118                 :   // aTagData must not need release (i.e. be a string from the text segment)
     119               0 :   ProfileEntry(char aTagName, const char *aTagData)
     120                 :     : mTagData(aTagData)
     121                 :     , mLeafAddress(0)
     122               0 :     , mTagName(aTagName)
     123               0 :   { }
     124                 : 
     125                 :   // aTagData must not need release (i.e. be a string from the text segment)
     126               0 :   ProfileEntry(char aTagName, const char *aTagData, Address aLeafAddress)
     127                 :     : mTagData(aTagData)
     128                 :     , mLeafAddress(aLeafAddress)
     129               0 :     , mTagName(aTagName)
     130               0 :   { }
     131                 : 
     132               0 :   ProfileEntry(char aTagName, double aTagFloat)
     133                 :     : mTagFloat(aTagFloat)
     134                 :     , mLeafAddress(0)
     135               0 :     , mTagName(aTagName)
     136               0 :   { }
     137                 : 
     138                 :   ProfileEntry(char aTagName, uintptr_t aTagOffset)
     139                 :     : mTagOffset(aTagOffset)
     140                 :     , mLeafAddress(0)
     141                 :     , mTagName(aTagName)
     142                 :   { }
     143                 : 
     144                 :   string TagToString(ThreadProfile *profile);
     145                 : 
     146                 : private:
     147                 :   friend class ThreadProfile;
     148                 :   union {
     149                 :     const char* mTagData;
     150                 :     double mTagFloat;
     151                 :     Address mTagAddress;
     152                 :     uintptr_t mTagOffset;
     153                 :   };
     154                 :   Address mLeafAddress;
     155                 :   char mTagName;
     156                 : };
     157                 : 
     158                 : #define PROFILE_MAX_ENTRY 100000
     159                 : class ThreadProfile
     160                 : {
     161                 : public:
     162               0 :   ThreadProfile(int aEntrySize, ProfileStack *aStack)
     163                 :     : mWritePos(0)
     164                 :     , mLastFlushPos(0)
     165                 :     , mReadPos(0)
     166                 :     , mEntrySize(aEntrySize)
     167               0 :     , mStack(aStack)
     168                 :   {
     169               0 :     mEntries = new ProfileEntry[mEntrySize];
     170               0 :   }
     171                 : 
     172               0 :   ~ThreadProfile()
     173                 :   {
     174               0 :     delete[] mEntries;
     175               0 :   }
     176                 : 
     177               0 :   void addTag(ProfileEntry aTag)
     178                 :   {
     179                 :     // Called from signal, call only reentrant functions
     180               0 :     mEntries[mWritePos] = aTag;
     181               0 :     mWritePos = (mWritePos + 1) % mEntrySize;
     182               0 :     if (mWritePos == mReadPos) {
     183                 :       // Keep one slot open
     184               0 :       mEntries[mReadPos] = ProfileEntry();
     185               0 :       mReadPos = (mReadPos + 1) % mEntrySize;
     186                 :     }
     187                 :     // we also need to move the flush pos to ensure we
     188                 :     // do not pass it
     189               0 :     if (mWritePos == mLastFlushPos) {
     190               0 :       mLastFlushPos = (mLastFlushPos + 1) % mEntrySize;
     191                 :     }
     192               0 :   }
     193                 : 
     194                 :   // flush the new entries
     195               0 :   void flush()
     196                 :   {
     197               0 :     mLastFlushPos = mWritePos;
     198               0 :   }
     199                 : 
     200                 :   // discards all of the entries since the last flush()
     201                 :   // NOTE: that if mWritePos happens to wrap around past
     202                 :   // mLastFlushPos we actually only discard mWritePos - mLastFlushPos entries
     203                 :   //
     204                 :   // r = mReadPos
     205                 :   // w = mWritePos
     206                 :   // f = mLastFlushPos
     207                 :   //
     208                 :   //     r          f    w
     209                 :   // |-----------------------------|
     210                 :   // |   abcdefghijklmnopq         | -> 'abcdefghijklmnopq'
     211                 :   // |-----------------------------|
     212                 :   //
     213                 :   //
     214                 :   // mWritePos and mReadPos have passed mLastFlushPos
     215                 :   //                      f
     216                 :   //                    w r
     217                 :   // |-----------------------------|
     218                 :   // |ABCDEFGHIJKLMNOPQRSqrstuvwxyz|
     219                 :   // |-----------------------------|
     220                 :   //                       w
     221                 :   //                       r
     222                 :   // |-----------------------------|
     223                 :   // |ABCDEFGHIJKLMNOPQRSqrstuvwxyz| -> ''
     224                 :   // |-----------------------------|
     225                 :   //
     226                 :   //
     227                 :   // mWritePos will end up the same as mReadPos
     228                 :   //                r
     229                 :   //              w f
     230                 :   // |-----------------------------|
     231                 :   // |ABCDEFGHIJKLMklmnopqrstuvwxyz|
     232                 :   // |-----------------------------|
     233                 :   //                r
     234                 :   //                w
     235                 :   // |-----------------------------|
     236                 :   // |ABCDEFGHIJKLMklmnopqrstuvwxyz| -> ''
     237                 :   // |-----------------------------|
     238                 :   //
     239                 :   //
     240                 :   // mWritePos has moved past mReadPos
     241                 :   //      w r       f
     242                 :   // |-----------------------------|
     243                 :   // |ABCDEFdefghijklmnopqrstuvwxyz|
     244                 :   // |-----------------------------|
     245                 :   //        r       w
     246                 :   // |-----------------------------|
     247                 :   // |ABCDEFdefghijklmnopqrstuvwxyz| -> 'defghijkl'
     248                 :   // |-----------------------------|
     249                 : 
     250               0 :   void erase()
     251                 :   {
     252               0 :     mWritePos = mLastFlushPos;
     253               0 :   }
     254                 : 
     255               0 :   void ToString(StringBuilder &profile)
     256                 :   {
     257               0 :     int readPos = mReadPos;
     258               0 :     while (readPos != mLastFlushPos) {
     259               0 :       profile.Append(mEntries[readPos].TagToString(this).c_str());
     260               0 :       readPos = (readPos + 1) % mEntrySize;
     261                 :     }
     262               0 :   }
     263                 : 
     264               0 :   JSObject *ToJSObject(JSContext *aCx)
     265                 :   {
     266               0 :     JSObjectBuilder b(aCx);
     267                 : 
     268               0 :     JSObject *profile = b.CreateObject();
     269               0 :     JSObject *samples = b.CreateArray();
     270               0 :     b.DefineProperty(profile, "samples", samples);
     271                 : 
     272               0 :     JSObject *sample = NULL;
     273               0 :     JSObject *frames = NULL;
     274                 : 
     275               0 :     int oldReadPos = mReadPos;
     276               0 :     while (mReadPos != mLastFlushPos) {
     277               0 :       ProfileEntry entry = mEntries[mReadPos];
     278               0 :       mReadPos = (mReadPos + 1) % mEntrySize;
     279               0 :       switch (entry.mTagName) {
     280                 :         case 's':
     281               0 :           sample = b.CreateObject();
     282               0 :           b.DefineProperty(sample, "name", (const char*)entry.mTagData);
     283               0 :           frames = b.CreateArray();
     284               0 :           b.DefineProperty(sample, "frames", frames);
     285               0 :           b.ArrayPush(samples, sample);
     286               0 :           break;
     287                 :         case 'c':
     288                 :         case 'l':
     289                 :           {
     290               0 :             if (sample) {
     291               0 :               JSObject *frame = b.CreateObject();
     292                 :               char tagBuff[1024];
     293               0 :               void* pc = (void*)entry.mTagData;
     294               0 :               snprintf(tagBuff, 1024, "%p", pc);
     295               0 :               b.DefineProperty(frame, "location", tagBuff);
     296               0 :               b.ArrayPush(frames, frame);
     297                 :             }
     298                 :           }
     299                 :       }
     300                 :     }
     301               0 :     mReadPos = oldReadPos;
     302                 : 
     303               0 :     return profile;
     304                 :   }
     305                 : 
     306               0 :   void WriteProfile(FILE* stream)
     307                 :   {
     308               0 :     int readPos = mReadPos;
     309               0 :     while (readPos != mLastFlushPos) {
     310               0 :       string tag = mEntries[readPos].TagToString(this);
     311               0 :       fwrite(tag.data(), 1, tag.length(), stream);
     312               0 :       readPos = (readPos + 1) % mEntrySize;
     313                 :     }
     314               0 :   }
     315                 : 
     316               0 :   ProfileStack* GetStack()
     317                 :   {
     318               0 :     return mStack;
     319                 :   }
     320                 : private:
     321                 :   // Circular buffer 'Keep One Slot Open' implementation
     322                 :   // for simplicity
     323                 :   ProfileEntry *mEntries;
     324                 :   int mWritePos; // points to the next entry we will write to
     325                 :   int mLastFlushPos; // points to the next entry since the last flush()
     326                 :   int mReadPos;  // points to the next entry we will read to
     327                 :   int mEntrySize;
     328                 :   ProfileStack *mStack;
     329                 : };
     330                 : 
     331                 : class SaveProfileTask;
     332                 : 
     333                 : static bool
     334               0 : hasFeature(const char** aFeatures, uint32_t aFeatureCount, const char* aFeature) {
     335               0 :   for(size_t i = 0; i < aFeatureCount; i++) {
     336               0 :     if (strcmp(aFeatures[i], aFeature) == 0)
     337               0 :       return true;
     338                 :   }
     339               0 :   return false;
     340                 : }
     341                 : 
     342                 : class TableTicker: public Sampler {
     343                 :  public:
     344               0 :   TableTicker(int aInterval, int aEntrySize, ProfileStack *aStack,
     345                 :               const char** aFeatures, uint32_t aFeatureCount)
     346                 :     : Sampler(aInterval, true)
     347                 :     , mPrimaryThreadProfile(aEntrySize, aStack)
     348               0 :     , mSaveRequested(false)
     349                 :   {
     350               0 :     mUseStackWalk = hasFeature(aFeatures, aFeatureCount, "stackwalk");
     351                 :     //XXX: It's probably worth splitting the jank profiler out from the regular profiler at some point
     352               0 :     mJankOnly = hasFeature(aFeatures, aFeatureCount, "jank");
     353               0 :     mPrimaryThreadProfile.addTag(ProfileEntry('m', "Start"));
     354               0 :   }
     355                 : 
     356               0 :   ~TableTicker() { if (IsActive()) Stop(); }
     357                 : 
     358               0 :   virtual void SampleStack(TickSample* sample) {}
     359                 : 
     360                 :   // Called within a signal. This function must be reentrant
     361                 :   virtual void Tick(TickSample* sample);
     362                 : 
     363                 :   // Called within a signal. This function must be reentrant
     364               0 :   virtual void RequestSave()
     365                 :   {
     366               0 :     mSaveRequested = true;
     367               0 :   }
     368                 : 
     369                 :   virtual void HandleSaveRequest();
     370                 : 
     371               0 :   ThreadProfile* GetPrimaryThreadProfile()
     372                 :   {
     373               0 :     return &mPrimaryThreadProfile;
     374                 :   }
     375                 : 
     376                 :   JSObject *ToJSObject(JSContext *aCx);
     377                 : 
     378                 : private:
     379                 :   // Not implemented on platforms which do not support backtracing
     380                 :   void doBacktrace(ThreadProfile &aProfile, Address pc);
     381                 : 
     382                 : private:
     383                 :   // This represent the application's main thread (SAMPLER_INIT)
     384                 :   ThreadProfile mPrimaryThreadProfile;
     385                 :   bool mSaveRequested;
     386                 :   bool mUseStackWalk;
     387                 :   bool mJankOnly;
     388                 : };
     389                 : 
     390                 : /**
     391                 :  * This is an event used to save the profile on the main thread
     392                 :  * to be sure that it is not being modified while saving.
     393                 :  */
     394               0 : class SaveProfileTask : public nsRunnable {
     395                 : public:
     396               0 :   SaveProfileTask() {}
     397                 : 
     398               0 :   NS_IMETHOD Run() {
     399               0 :     TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
     400                 : 
     401                 :     char buff[MAXPATHLEN];
     402                 : #ifdef ANDROID
     403                 :   #define FOLDER "/sdcard/"
     404                 : #elif defined(XP_WIN)
     405                 :   #define FOLDER "%TEMP%\\"
     406                 : #else
     407                 :   #define FOLDER "/tmp/"
     408                 : #endif
     409                 : 
     410               0 :     snprintf(buff, MAXPATHLEN, "%sprofile_%i_%i.txt", FOLDER, XRE_GetProcessType(), getpid());
     411                 : 
     412                 : #ifdef XP_WIN
     413                 :     // Expand %TEMP% on Windows
     414                 :     {
     415                 :       char tmp[MAXPATHLEN];
     416                 :       ExpandEnvironmentStringsA(buff, tmp, mozilla::ArrayLength(tmp));
     417                 :       strcpy(buff, tmp);
     418                 :     }
     419                 : #endif
     420                 : 
     421               0 :     FILE* stream = ::fopen(buff, "w");
     422               0 :     if (stream) {
     423               0 :       t->GetPrimaryThreadProfile()->WriteProfile(stream);
     424               0 :       ::fclose(stream);
     425               0 :       LOG("Saved to " FOLDER "profile_TYPE_PID.txt");
     426                 :     } else {
     427               0 :       LOG("Fail to open profile log file.");
     428                 :     }
     429                 : 
     430               0 :     return NS_OK;
     431                 :   }
     432                 : };
     433                 : 
     434               0 : void TableTicker::HandleSaveRequest()
     435                 : {
     436               0 :   if (!mSaveRequested)
     437               0 :     return;
     438               0 :   mSaveRequested = false;
     439                 : 
     440                 :   // TODO: Use use the ipc/chromium Tasks here to support processes
     441                 :   // without XPCOM.
     442               0 :   nsCOMPtr<nsIRunnable> runnable = new SaveProfileTask();
     443               0 :   NS_DispatchToMainThread(runnable);
     444                 : }
     445                 : 
     446               0 : JSObject* TableTicker::ToJSObject(JSContext *aCx)
     447                 : {
     448               0 :   JSObjectBuilder b(aCx);
     449                 : 
     450               0 :   JSObject *profile = b.CreateObject();
     451                 : 
     452                 :   // Put meta data
     453                 :   // TODO: List things like feature, version number, profile time start/end
     454                 : 
     455                 :   // Lists the samples for each ThreadProfile
     456               0 :   JSObject *threads = b.CreateArray();
     457               0 :   b.DefineProperty(profile, "threads", threads);
     458                 : 
     459                 :   // For now we only have one thread
     460               0 :   JSObject* threadSamples = GetPrimaryThreadProfile()->ToJSObject(aCx);
     461               0 :   b.ArrayPush(threads, threadSamples);
     462                 : 
     463               0 :   return profile;
     464                 : }
     465                 : 
     466                 : 
     467                 : #ifdef USE_BACKTRACE
     468                 : void TableTicker::doBacktrace(ThreadProfile &aProfile, Address pc)
     469                 : {
     470                 :   void *array[100];
     471                 :   int count = backtrace (array, 100);
     472                 : 
     473                 :   aProfile.addTag(ProfileEntry('s', "(root)", 0));
     474                 : 
     475                 :   for (int i = 0; i < count; i++) {
     476                 :     if( (intptr_t)array[i] == -1 ) break;
     477                 :     aProfile.addTag(ProfileEntry('l', (const char*)array[i]));
     478                 :   }
     479                 : }
     480                 : #endif
     481                 : 
     482                 : 
     483                 : #ifdef USE_NS_STACKWALK
     484                 : typedef struct {
     485                 :   void** array;
     486                 :   size_t size;
     487                 :   size_t count;
     488                 : } PCArray;
     489                 : 
     490                 : static
     491                 : void StackWalkCallback(void* aPC, void* aClosure)
     492                 : {
     493                 :   PCArray* array = static_cast<PCArray*>(aClosure);
     494                 :   if (array->count >= array->size) {
     495                 :     // too many frames, ignore
     496                 :     return;
     497                 :   }
     498                 :   array->array[array->count++] = aPC;
     499                 : }
     500                 : 
     501                 : void TableTicker::doBacktrace(ThreadProfile &aProfile, Address fp)
     502                 : {
     503                 : #ifndef XP_MACOSX
     504                 :   uintptr_t thread = GetThreadHandle(platform_data());
     505                 :   MOZ_ASSERT(thread);
     506                 : #endif
     507                 :   void* pc_array[1000];
     508                 :   PCArray array = {
     509                 :     pc_array,
     510                 :     mozilla::ArrayLength(pc_array),
     511                 :     0
     512                 :   };
     513                 : #ifdef XP_MACOSX
     514                 :   pthread_t pt = GetProfiledThread(platform_data());
     515                 :   void *stackEnd = reinterpret_cast<void*>(-1);
     516                 :   if (pt)
     517                 :     stackEnd = static_cast<char*>(pthread_get_stackaddr_np(pt));
     518                 :   nsresult rv = FramePointerStackWalk(StackWalkCallback, 1, &array, reinterpret_cast<void**>(fp), stackEnd);
     519                 : #else
     520                 :   nsresult rv = NS_StackWalk(StackWalkCallback, 0, &array, thread);
     521                 : #endif
     522                 :   if (NS_SUCCEEDED(rv)) {
     523                 :     aProfile.addTag(ProfileEntry('s', "(root)", 0));
     524                 : 
     525                 :     for (size_t i = array.count; i > 0; --i) {
     526                 :       aProfile.addTag(ProfileEntry('l', (const char*)array.array[i - 1]));
     527                 :     }
     528                 :   }
     529                 : }
     530                 : #endif
     531                 : 
     532                 : static
     533               0 : void doSampleStackTrace(ProfileStack *aStack, ThreadProfile &aProfile, TickSample *sample)
     534                 : {
     535                 :   // Sample
     536                 :   // 's' tag denotes the start of a sample block
     537                 :   // followed by 0 or more 'c' tags.
     538               0 :   for (int i = 0; i < aStack->mStackPointer; i++) {
     539               0 :     if (i == 0) {
     540               0 :       Address pc = 0;
     541               0 :       if (sample) {
     542               0 :         pc = sample->pc;
     543                 :       }
     544               0 :       aProfile.addTag(ProfileEntry('s', aStack->mStack[i], pc));
     545                 :     } else {
     546               0 :       aProfile.addTag(ProfileEntry('c', aStack->mStack[i]));
     547                 :     }
     548                 :   }
     549               0 : }
     550                 : 
     551                 : /* used to keep track of the last event that we sampled during */
     552                 : unsigned int sLastSampledEventGeneration = 0;
     553                 : 
     554                 : /* a counter that's incremented everytime we get responsiveness event
     555                 :  * note: it might also be worth tracking everytime we go around
     556                 :  * the event loop */
     557                 : unsigned int sCurrentEventGeneration = 0;
     558                 : /* we don't need to worry about overflow because we only treat the
     559                 :  * case of them being the same as special. i.e. we only run into
     560                 :  * a problem if 2^32 events happen between samples that we need
     561                 :  * to know are associated with different events */
     562                 : 
     563               0 : void TableTicker::Tick(TickSample* sample)
     564                 : {
     565                 :   // Marker(s) come before the sample
     566               0 :   ProfileStack* stack = mPrimaryThreadProfile.GetStack();
     567               0 :   for (int i = 0; stack->getMarker(i) != NULL; i++) {
     568               0 :     mPrimaryThreadProfile.addTag(ProfileEntry('m', stack->getMarker(i)));
     569                 :   }
     570               0 :   stack->mQueueClearMarker = true;
     571                 : 
     572               0 :   bool recordSample = true;
     573               0 :   if (mJankOnly) {
     574                 :     // if we are on a different event we can discard any temporary samples
     575                 :     // we've kept around
     576               0 :     if (sLastSampledEventGeneration != sCurrentEventGeneration) {
     577                 :       // XXX: we also probably want to add an entry to the profile to help
     578                 :       // distinguish which samples are part of the same event. That, or record
     579                 :       // the event generation in each sample
     580               0 :       mPrimaryThreadProfile.erase();
     581                 :     }
     582               0 :     sLastSampledEventGeneration = sCurrentEventGeneration;
     583                 : 
     584               0 :     recordSample = false;
     585                 :     // only record the events when we have a we haven't seen a tracer event for 100ms
     586               0 :     if (!sLastTracerEvent.IsNull()) {
     587               0 :       TimeDuration delta = sample->timestamp - sLastTracerEvent;
     588               0 :       if (delta.ToMilliseconds() > 100.0) {
     589               0 :           recordSample = true;
     590                 :       }
     591                 :     }
     592                 :   }
     593                 : 
     594                 : #if defined(USE_BACKTRACE) || defined(USE_NS_STACKWALK)
     595                 :   if (mUseStackWalk) {
     596                 :     doBacktrace(mPrimaryThreadProfile, sample->fp);
     597                 :   } else {
     598                 :     doSampleStackTrace(stack, mPrimaryThreadProfile, sample);
     599                 :   }
     600                 : #else
     601               0 :   doSampleStackTrace(stack, mPrimaryThreadProfile, sample);
     602                 : #endif
     603                 : 
     604               0 :   if (recordSample)
     605               0 :     mPrimaryThreadProfile.flush();
     606                 : 
     607               0 :   if (!mJankOnly && !sLastTracerEvent.IsNull() && sample) {
     608               0 :     TimeDuration delta = sample->timestamp - sLastTracerEvent;
     609               0 :     mPrimaryThreadProfile.addTag(ProfileEntry('r', delta.ToMilliseconds()));
     610                 :   }
     611               0 : }
     612                 : 
     613               0 : string ProfileEntry::TagToString(ThreadProfile *profile)
     614                 : {
     615               0 :   string tag = "";
     616               0 :   if (mTagName == 'r') {
     617                 :     char buff[50];
     618               0 :     snprintf(buff, 50, "%-40f", mTagFloat);
     619               0 :     tag += string(1, mTagName) + string("-") + string(buff) + string("\n");
     620               0 :   } else if (mTagName == 'l') {
     621                 :     char tagBuff[1024];
     622               0 :     Address pc = mTagAddress;
     623               0 :     snprintf(tagBuff, 1024, "l-%p\n", pc);
     624               0 :     tag += string(tagBuff);
     625                 :   } else {
     626               0 :     tag += string(1, mTagName) + string("-") + string(mTagData) + string("\n");
     627                 :   }
     628                 : 
     629                 : #ifdef ENABLE_SPS_LEAF_DATA
     630                 :   if (mLeafAddress) {
     631                 :     char tagBuff[1024];
     632                 :     unsigned long pc = (unsigned long)mLeafAddress;
     633                 :     snprintf(tagBuff, 1024, "l-%llu\n", pc);
     634                 :     tag += string(tagBuff);
     635                 :   }
     636                 : #endif
     637                 :   return tag;
     638                 : }
     639                 : 
     640                 : #define PROFILE_DEFAULT_ENTRY 100000
     641                 : #define PROFILE_DEFAULT_INTERVAL 10
     642                 : #define PROFILE_DEFAULT_FEATURES NULL
     643                 : #define PROFILE_DEFAULT_FEATURE_COUNT 0
     644                 : 
     645              23 : void mozilla_sampler_init()
     646                 : {
     647                 :   // TODO linux port: Use TLS with ifdefs
     648              46 :   if (!mozilla::tls::create(&pkey_stack) ||
     649              23 :       !mozilla::tls::create(&pkey_ticker)) {
     650               0 :     LOG("Failed to init.");
     651               0 :     return;
     652                 :   }
     653              23 :   stack_key_initialized = true;
     654                 : 
     655              23 :   ProfileStack *stack = new ProfileStack();
     656              23 :   mozilla::tls::set(pkey_stack, stack);
     657                 : 
     658                 :   // We can't open pref so we use an environment variable
     659                 :   // to know if we should trigger the profiler on startup
     660                 :   // NOTE: Default
     661              23 :   const char *val = PR_GetEnv("MOZ_PROFILER_STARTUP");
     662              23 :   if (!val || !*val) {
     663              23 :     return;
     664                 :   }
     665                 : 
     666                 :   mozilla_sampler_start(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL,
     667               0 :                         PROFILE_DEFAULT_FEATURES, PROFILE_DEFAULT_FEATURE_COUNT);
     668                 : }
     669                 : 
     670               0 : void mozilla_sampler_deinit()
     671                 : {
     672               0 :   mozilla_sampler_stop();
     673                 :   // We can't delete the Stack because we can be between a
     674                 :   // sampler call_enter/call_exit point.
     675                 :   // TODO Need to find a safe time to delete Stack
     676               0 : }
     677                 : 
     678               0 : void mozilla_sampler_save()
     679                 : {
     680               0 :   TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
     681               0 :   if (!t) {
     682               0 :     return;
     683                 :   }
     684                 : 
     685               0 :   t->RequestSave();
     686                 :   // We're on the main thread already so we don't
     687                 :   // have to wait to handle the save request.
     688               0 :   t->HandleSaveRequest();
     689                 : }
     690                 : 
     691               0 : char* mozilla_sampler_get_profile()
     692                 : {
     693               0 :   TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
     694               0 :   if (!t) {
     695               0 :     return NULL;
     696                 :   }
     697                 : 
     698               0 :   StringBuilder profile;
     699               0 :   t->GetPrimaryThreadProfile()->ToString(profile);
     700                 : 
     701               0 :   char *rtn = (char*)malloc( (profile.Length()+1) * sizeof(char) );
     702               0 :   strcpy(rtn, profile.Buffer());
     703               0 :   return rtn;
     704                 : }
     705                 : 
     706               0 : JSObject *mozilla_sampler_get_profile_data(JSContext *aCx)
     707                 : {
     708               0 :   TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
     709               0 :   if (!t) {
     710               0 :     return NULL;
     711                 :   }
     712                 : 
     713               0 :   return t->ToJSObject(aCx);
     714                 : }
     715                 : 
     716                 : 
     717               0 : const char** mozilla_sampler_get_features()
     718                 : {
     719                 :   static const char* features[] = {
     720                 : #if defined(MOZ_PROFILING) && (defined(USE_BACKTRACE) || defined(USE_NS_STACKWALK))
     721                 :     "stackwalk",
     722                 : #endif
     723                 :     "jank",
     724                 :     NULL
     725                 :   };
     726                 : 
     727               0 :   return features;
     728                 : }
     729                 : 
     730                 : // Values are only honored on the first start
     731               0 : void mozilla_sampler_start(int aProfileEntries, int aInterval,
     732                 :                            const char** aFeatures, uint32_t aFeatureCount)
     733                 : {
     734               0 :   ProfileStack *stack = mozilla::tls::get<ProfileStack>(pkey_stack);
     735               0 :   if (!stack) {
     736               0 :     ASSERT(false);
     737               0 :     return;
     738                 :   }
     739                 : 
     740               0 :   mozilla_sampler_stop();
     741                 : 
     742                 :   TableTicker *t = new TableTicker(aInterval, aProfileEntries, stack,
     743               0 :                                    aFeatures, aFeatureCount);
     744               0 :   mozilla::tls::set(pkey_ticker, t);
     745               0 :   t->Start();
     746                 : }
     747                 : 
     748               0 : void mozilla_sampler_stop()
     749                 : {
     750               0 :   TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
     751               0 :   if (!t) {
     752               0 :     return;
     753                 :   }
     754                 : 
     755               0 :   t->Stop();
     756               0 :   mozilla::tls::set(pkey_ticker, (ProfileStack*)NULL);
     757                 : }
     758                 : 
     759               0 : bool mozilla_sampler_is_active()
     760                 : {
     761               0 :   TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
     762               0 :   if (!t) {
     763               0 :     return false;
     764                 :   }
     765                 : 
     766               0 :   return t->IsActive();
     767                 : }
     768                 : 
     769                 : double sResponsivenessTimes[100];
     770                 : double sCurrResponsiveness = 0.f;
     771                 : unsigned int sResponsivenessLoc = 0;
     772               0 : void mozilla_sampler_responsiveness(TimeStamp aTime)
     773                 : {
     774               0 :   if (!sLastTracerEvent.IsNull()) {
     775               0 :     if (sResponsivenessLoc == 100) {
     776               0 :       for(size_t i = 0; i < 100-1; i++) {
     777               0 :         sResponsivenessTimes[i] = sResponsivenessTimes[i+1];
     778                 :       }
     779               0 :       sResponsivenessLoc--;
     780                 :     }
     781               0 :     TimeDuration delta = aTime - sLastTracerEvent;
     782               0 :     sResponsivenessTimes[sResponsivenessLoc++] = delta.ToMilliseconds();
     783                 :   }
     784               0 :   sCurrentEventGeneration++;
     785                 : 
     786               0 :   sLastTracerEvent = aTime;
     787               0 : }
     788                 : 
     789               0 : const double* mozilla_sampler_get_responsiveness()
     790                 : {
     791               0 :   return sResponsivenessTimes;
     792            4392 : }
     793                 : 

Generated by: LCOV version 1.7