2011-08-27 00:05:37 +00:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is mozilla.org code.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Mozilla Foundation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2011
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Benoit Girard <bgirard@mozilla.com>
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
2011-12-07 19:48:15 +00:00
|
|
|
#include <string>
|
2011-08-27 00:05:37 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include "sps_sampler.h"
|
|
|
|
#include "platform.h"
|
|
|
|
#include "nsXULAppAPI.h"
|
|
|
|
#include "nsThreadUtils.h"
|
|
|
|
#include "prenv.h"
|
2011-12-15 12:31:41 +00:00
|
|
|
#include "shared-libraries.h"
|
2011-12-16 14:03:54 +00:00
|
|
|
#include "mozilla/StringBuilder.h"
|
2011-08-27 00:05:37 +00:00
|
|
|
|
2011-12-20 01:33:00 +00:00
|
|
|
// we eventually want to make this runtime switchable
|
|
|
|
//#define USE_BACKTRACE
|
|
|
|
#ifdef USE_BACKTRACE
|
|
|
|
#include <execinfo.h>
|
|
|
|
#endif
|
|
|
|
|
2011-12-07 19:48:15 +00:00
|
|
|
using std::string;
|
2011-12-16 14:03:54 +00:00
|
|
|
using namespace mozilla;
|
2011-12-07 19:48:15 +00:00
|
|
|
|
2011-12-04 19:09:00 +00:00
|
|
|
#ifdef XP_WIN
|
|
|
|
#include <windows.h>
|
|
|
|
#define getpid GetCurrentProcessId
|
|
|
|
#else
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef MAXPATHLEN
|
|
|
|
#ifdef PATH_MAX
|
|
|
|
#define MAXPATHLEN PATH_MAX
|
|
|
|
#elif defined(MAX_PATH)
|
|
|
|
#define MAXPATHLEN MAX_PATH
|
|
|
|
#elif defined(_MAX_PATH)
|
|
|
|
#define MAXPATHLEN _MAX_PATH
|
|
|
|
#elif defined(CCHMAXPATH)
|
|
|
|
#define MAXPATHLEN CCHMAXPATH
|
|
|
|
#else
|
|
|
|
#define MAXPATHLEN 1024
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if _MSC_VER
|
|
|
|
#define snprintf _snprintf
|
|
|
|
#endif
|
|
|
|
|
2011-12-20 01:33:00 +00:00
|
|
|
|
2011-12-04 19:09:00 +00:00
|
|
|
mozilla::tls::key pkey_stack;
|
|
|
|
mozilla::tls::key pkey_ticker;
|
2011-12-08 15:46:02 +00:00
|
|
|
// We need to track whether we've been initialized otherwise
|
|
|
|
// we end up using pkey_stack without initializing it.
|
|
|
|
// Because pkey_stack is totally opaque to us we can't reuse
|
|
|
|
// it as the flag itself.
|
|
|
|
bool stack_key_initialized;
|
2011-08-27 00:05:37 +00:00
|
|
|
|
2011-12-07 19:48:15 +00:00
|
|
|
TimeStamp sLastTracerEvent;
|
|
|
|
|
2011-08-27 00:05:37 +00:00
|
|
|
class Profile;
|
|
|
|
|
|
|
|
class ProfileEntry
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
ProfileEntry()
|
|
|
|
: mTagData(NULL)
|
|
|
|
, mLeafAddress(0)
|
|
|
|
, mTagName(0)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
// aTagData must not need release (i.e. be a string from the text segment)
|
|
|
|
ProfileEntry(char aTagName, const char *aTagData)
|
|
|
|
: mTagData(aTagData)
|
|
|
|
, mLeafAddress(0)
|
|
|
|
, mTagName(aTagName)
|
|
|
|
{ }
|
|
|
|
|
2011-12-07 19:48:15 +00:00
|
|
|
// aTagData must not need release (i.e. be a string from the text segment)
|
2011-08-27 00:05:37 +00:00
|
|
|
ProfileEntry(char aTagName, const char *aTagData, Address aLeafAddress)
|
|
|
|
: mTagData(aTagData)
|
|
|
|
, mLeafAddress(aLeafAddress)
|
|
|
|
, mTagName(aTagName)
|
|
|
|
{ }
|
|
|
|
|
2012-01-12 16:50:43 +00:00
|
|
|
ProfileEntry(char aTagName, double aTagFloat)
|
2011-12-07 19:48:15 +00:00
|
|
|
: mTagFloat(aTagFloat)
|
|
|
|
, mLeafAddress(0)
|
|
|
|
, mTagName(aTagName)
|
|
|
|
{ }
|
|
|
|
|
|
|
|
string TagToString(Profile *profile);
|
2011-08-27 00:05:37 +00:00
|
|
|
|
|
|
|
private:
|
2011-12-07 19:48:15 +00:00
|
|
|
union {
|
|
|
|
const char* mTagData;
|
2012-01-12 16:50:43 +00:00
|
|
|
double mTagFloat;
|
2011-12-20 01:33:00 +00:00
|
|
|
Address mTagAddress;
|
2011-12-07 19:48:15 +00:00
|
|
|
};
|
2011-08-27 00:05:37 +00:00
|
|
|
Address mLeafAddress;
|
|
|
|
char mTagName;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define PROFILE_MAX_ENTRY 100000
|
|
|
|
class Profile
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
Profile(int aEntrySize)
|
|
|
|
: mWritePos(0)
|
|
|
|
, mReadPos(0)
|
|
|
|
, mEntrySize(aEntrySize)
|
|
|
|
{
|
|
|
|
mEntries = new ProfileEntry[mEntrySize];
|
2011-12-16 16:54:22 +00:00
|
|
|
mNeedsSharedLibraryInfo = false;
|
2011-12-20 01:33:00 +00:00
|
|
|
#if defined(ENABLE_SPS_LEAF_DATA) || defined(USE_BACKTRACE)
|
2011-12-16 16:54:22 +00:00
|
|
|
mNeedsSharedLibraryInfo = true;
|
|
|
|
#endif
|
2011-08-27 00:05:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
~Profile()
|
|
|
|
{
|
|
|
|
delete[] mEntries;
|
|
|
|
}
|
|
|
|
|
|
|
|
void addTag(ProfileEntry aTag)
|
|
|
|
{
|
|
|
|
// Called from signal, call only reentrant functions
|
|
|
|
mEntries[mWritePos] = aTag;
|
|
|
|
mWritePos = (mWritePos + 1) % mEntrySize;
|
|
|
|
if (mWritePos == mReadPos) {
|
|
|
|
// Keep one slot open
|
|
|
|
mEntries[mReadPos] = ProfileEntry();
|
|
|
|
mReadPos = (mReadPos + 1) % mEntrySize;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-16 14:03:54 +00:00
|
|
|
void ToString(StringBuilder &profile)
|
2011-12-07 19:48:15 +00:00
|
|
|
{
|
2011-12-16 16:54:22 +00:00
|
|
|
if (mNeedsSharedLibraryInfo) {
|
|
|
|
// Can't be called from signal because
|
|
|
|
// getting the shared library information can call non-reentrant functions.
|
|
|
|
mSharedLibraryInfo = SharedLibraryInfo::GetInfoForSelf();
|
|
|
|
}
|
2011-12-07 19:48:15 +00:00
|
|
|
|
|
|
|
int oldReadPos = mReadPos;
|
|
|
|
while (mReadPos != mWritePos) {
|
2011-12-16 14:03:54 +00:00
|
|
|
profile.Append(mEntries[mReadPos].TagToString(this).c_str());
|
2011-12-07 19:48:15 +00:00
|
|
|
mReadPos = (mReadPos + 1) % mEntrySize;
|
|
|
|
}
|
|
|
|
mReadPos = oldReadPos;
|
|
|
|
}
|
|
|
|
|
2011-08-27 00:05:37 +00:00
|
|
|
void WriteProfile(FILE* stream)
|
|
|
|
{
|
2011-12-16 16:54:22 +00:00
|
|
|
if (mNeedsSharedLibraryInfo) {
|
|
|
|
// Can't be called from signal because
|
|
|
|
// getting the shared library information can call non-reentrant functions.
|
|
|
|
mSharedLibraryInfo = SharedLibraryInfo::GetInfoForSelf();
|
|
|
|
}
|
2011-08-27 00:05:37 +00:00
|
|
|
|
|
|
|
int oldReadPos = mReadPos;
|
|
|
|
while (mReadPos != mWritePos) {
|
2011-12-16 14:12:46 +00:00
|
|
|
string tag = mEntries[mReadPos].TagToString(this);
|
|
|
|
fwrite(tag.data(), 1, tag.length(), stream);
|
2011-08-27 00:05:37 +00:00
|
|
|
mReadPos = (mReadPos + 1) % mEntrySize;
|
|
|
|
}
|
|
|
|
mReadPos = oldReadPos;
|
|
|
|
}
|
|
|
|
|
2011-12-15 12:31:41 +00:00
|
|
|
SharedLibraryInfo& getSharedLibraryInfo()
|
2011-08-27 00:05:37 +00:00
|
|
|
{
|
2011-12-15 12:31:41 +00:00
|
|
|
return mSharedLibraryInfo;
|
2011-08-27 00:05:37 +00:00
|
|
|
}
|
|
|
|
private:
|
|
|
|
// Circular buffer 'Keep One Slot Open' implementation
|
|
|
|
// for simplicity
|
|
|
|
ProfileEntry *mEntries;
|
|
|
|
int mWritePos; // points to the next entry we will write to
|
|
|
|
int mReadPos; // points to the next entry we will read to
|
|
|
|
int mEntrySize;
|
2011-12-16 16:54:22 +00:00
|
|
|
bool mNeedsSharedLibraryInfo;
|
2011-12-15 12:31:41 +00:00
|
|
|
SharedLibraryInfo mSharedLibraryInfo;
|
2011-08-27 00:05:37 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class SaveProfileTask;
|
|
|
|
|
|
|
|
class TableTicker: public Sampler {
|
|
|
|
public:
|
2011-12-07 19:48:15 +00:00
|
|
|
explicit TableTicker(int aInterval, int aEntrySize, Stack *aStack)
|
2011-08-27 00:05:37 +00:00
|
|
|
: Sampler(aInterval, true)
|
|
|
|
, mProfile(aEntrySize)
|
2011-12-07 19:48:15 +00:00
|
|
|
, mStack(aStack)
|
2011-08-27 00:05:37 +00:00
|
|
|
, mSaveRequested(false)
|
|
|
|
{
|
|
|
|
mProfile.addTag(ProfileEntry('m', "Start"));
|
|
|
|
}
|
|
|
|
|
|
|
|
~TableTicker() { if (IsActive()) Stop(); }
|
|
|
|
|
|
|
|
virtual void SampleStack(TickSample* sample) {}
|
|
|
|
|
|
|
|
// Called within a signal. This function must be reentrant
|
|
|
|
virtual void Tick(TickSample* sample);
|
|
|
|
|
|
|
|
// Called within a signal. This function must be reentrant
|
|
|
|
virtual void RequestSave()
|
|
|
|
{
|
|
|
|
mSaveRequested = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void HandleSaveRequest();
|
|
|
|
|
|
|
|
Stack* GetStack()
|
|
|
|
{
|
2011-12-07 19:48:15 +00:00
|
|
|
return mStack;
|
2011-08-27 00:05:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Profile* GetProfile()
|
|
|
|
{
|
|
|
|
return &mProfile;
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
Profile mProfile;
|
2011-12-07 19:48:15 +00:00
|
|
|
Stack *mStack;
|
2011-08-27 00:05:37 +00:00
|
|
|
bool mSaveRequested;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This is an event used to save the profile on the main thread
|
|
|
|
* to be sure that it is not being modified while saving.
|
|
|
|
*/
|
|
|
|
class SaveProfileTask : public nsRunnable {
|
|
|
|
public:
|
|
|
|
SaveProfileTask() {}
|
|
|
|
|
|
|
|
NS_IMETHOD Run() {
|
2011-12-04 19:09:00 +00:00
|
|
|
TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
|
2011-08-27 00:05:37 +00:00
|
|
|
|
2011-12-04 19:09:00 +00:00
|
|
|
char buff[MAXPATHLEN];
|
2011-08-27 00:05:37 +00:00
|
|
|
#ifdef ANDROID
|
|
|
|
#define FOLDER "/sdcard/"
|
2011-12-04 19:09:00 +00:00
|
|
|
#elif defined(XP_WIN)
|
|
|
|
#define FOLDER "%TEMP%\\"
|
2011-08-27 00:05:37 +00:00
|
|
|
#else
|
|
|
|
#define FOLDER "/tmp/"
|
|
|
|
#endif
|
2011-12-04 19:09:00 +00:00
|
|
|
|
|
|
|
snprintf(buff, MAXPATHLEN, "%sprofile_%i_%i.txt", FOLDER, XRE_GetProcessType(), getpid());
|
|
|
|
|
|
|
|
#ifdef XP_WIN
|
|
|
|
// Expand %TEMP% on Windows
|
|
|
|
{
|
|
|
|
char tmp[MAXPATHLEN];
|
|
|
|
ExpandEnvironmentStringsA(buff, tmp, mozilla::ArrayLength(tmp));
|
|
|
|
strcpy(buff, tmp);
|
|
|
|
}
|
|
|
|
#endif
|
2011-08-27 00:05:37 +00:00
|
|
|
|
|
|
|
FILE* stream = ::fopen(buff, "w");
|
|
|
|
if (stream) {
|
|
|
|
t->GetProfile()->WriteProfile(stream);
|
|
|
|
::fclose(stream);
|
2011-12-07 19:48:15 +00:00
|
|
|
LOG("Saved to " FOLDER "profile_TYPE_PID.txt");
|
2011-08-27 00:05:37 +00:00
|
|
|
} else {
|
|
|
|
LOG("Fail to open profile log file.");
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void TableTicker::HandleSaveRequest()
|
|
|
|
{
|
|
|
|
if (!mSaveRequested)
|
|
|
|
return;
|
|
|
|
mSaveRequested = false;
|
|
|
|
|
|
|
|
// TODO: Use use the ipc/chromium Tasks here to support processes
|
|
|
|
// without XPCOM.
|
|
|
|
nsCOMPtr<nsIRunnable> runnable = new SaveProfileTask();
|
|
|
|
NS_DispatchToMainThread(runnable);
|
|
|
|
}
|
|
|
|
|
2011-12-20 01:33:00 +00:00
|
|
|
#ifdef USE_BACKTRACE
|
|
|
|
static
|
|
|
|
void doBacktrace(Profile &aProfile)
|
2011-08-27 00:05:37 +00:00
|
|
|
{
|
2011-12-20 01:33:00 +00:00
|
|
|
void *array[100];
|
|
|
|
int count = backtrace (array, 100);
|
|
|
|
|
|
|
|
bool isSignal = true;
|
|
|
|
#ifndef __i386__
|
|
|
|
// the test doesn't work for 64bit
|
|
|
|
isSignal = false;
|
|
|
|
#endif
|
|
|
|
for (int i = count-1; i >= 0; i--) {
|
|
|
|
if( isSignal ) {
|
|
|
|
if( (intptr_t)array[i] == -1 ) { // signal frames have addresses of -1?
|
|
|
|
isSignal = false;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
aProfile.addTag(ProfileEntry('l', (const char*)array[i]));
|
2011-08-27 00:05:37 +00:00
|
|
|
}
|
2011-12-20 01:33:00 +00:00
|
|
|
aProfile.addTag(ProfileEntry('s', "XRE_Main", 0));
|
|
|
|
}
|
|
|
|
#endif
|
2011-08-27 00:05:37 +00:00
|
|
|
|
2011-12-20 01:33:00 +00:00
|
|
|
static
|
|
|
|
void doSampleStackTrace(Stack *aStack, Profile &aProfile, TickSample *sample)
|
|
|
|
{
|
2011-08-27 00:05:37 +00:00
|
|
|
// Sample
|
|
|
|
// 's' tag denotes the start of a sample block
|
|
|
|
// followed by 0 or more 'c' tags.
|
2011-12-20 01:33:00 +00:00
|
|
|
for (int i = 0; i < aStack->mStackPointer; i++) {
|
2011-08-27 00:05:37 +00:00
|
|
|
if (i == 0) {
|
|
|
|
Address pc = 0;
|
|
|
|
if (sample) {
|
|
|
|
pc = sample->pc;
|
|
|
|
}
|
2011-12-20 01:33:00 +00:00
|
|
|
aProfile.addTag(ProfileEntry('s', aStack->mStack[i], pc));
|
2011-08-27 00:05:37 +00:00
|
|
|
} else {
|
2011-12-20 01:33:00 +00:00
|
|
|
aProfile.addTag(ProfileEntry('c', aStack->mStack[i]));
|
2011-08-27 00:05:37 +00:00
|
|
|
}
|
|
|
|
}
|
2011-12-20 01:33:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TableTicker::Tick(TickSample* sample)
|
|
|
|
{
|
|
|
|
// Marker(s) come before the sample
|
|
|
|
int i = 0;
|
|
|
|
const char *marker = mStack->getMarker(i++);
|
|
|
|
for (int i = 0; marker != NULL; i++) {
|
|
|
|
mProfile.addTag(ProfileEntry('m', marker));
|
|
|
|
marker = mStack->getMarker(i++);
|
|
|
|
}
|
|
|
|
mStack->mQueueClearMarker = true;
|
|
|
|
|
|
|
|
#ifdef USE_BACKTRACE
|
|
|
|
doBacktrace(mProfile);
|
|
|
|
#else
|
|
|
|
doSampleStackTrace(mStack, mProfile, sample);
|
|
|
|
#endif
|
2011-12-07 19:48:15 +00:00
|
|
|
|
|
|
|
if (!sLastTracerEvent.IsNull()) {
|
2011-12-24 17:11:26 +00:00
|
|
|
TimeDuration delta = sample->timestamp - sLastTracerEvent;
|
2011-12-07 19:48:15 +00:00
|
|
|
mProfile.addTag(ProfileEntry('r', delta.ToMilliseconds()));
|
|
|
|
}
|
2011-08-27 00:05:37 +00:00
|
|
|
}
|
|
|
|
|
2011-12-07 19:48:15 +00:00
|
|
|
string ProfileEntry::TagToString(Profile *profile)
|
|
|
|
{
|
|
|
|
string tag = "";
|
|
|
|
if (mTagName == 'r') {
|
|
|
|
char buff[50];
|
|
|
|
snprintf(buff, 50, "%-40f", mTagFloat);
|
|
|
|
tag += string(1, mTagName) + string("-") + string(buff) + string("\n");
|
2011-12-20 01:33:00 +00:00
|
|
|
} else if (mTagName == 'l') {
|
|
|
|
bool found = false;
|
|
|
|
char tagBuff[1024];
|
|
|
|
SharedLibraryInfo& shlibInfo = profile->getSharedLibraryInfo();
|
|
|
|
Address pc = mTagAddress;
|
|
|
|
// TODO Use binary sort (STL)
|
|
|
|
for (size_t i = 0; i < shlibInfo.GetSize(); i++) {
|
|
|
|
SharedLibrary &e = shlibInfo.GetEntry(i);
|
|
|
|
if (pc > (Address)e.GetStart() && pc < (Address)e.GetEnd()) {
|
|
|
|
if (e.GetName()) {
|
|
|
|
found = true;
|
|
|
|
snprintf(tagBuff, 1024, "l-%s@%p\n", e.GetName(), pc - e.GetStart());
|
|
|
|
tag += string(tagBuff);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found) {
|
|
|
|
snprintf(tagBuff, 1024, "l-???@%p\n", pc);
|
|
|
|
tag += string(tagBuff);
|
|
|
|
}
|
2011-12-07 19:48:15 +00:00
|
|
|
} else {
|
|
|
|
tag += string(1, mTagName) + string("-") + string(mTagData) + string("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef ENABLE_SPS_LEAF_DATA
|
|
|
|
if (mLeafAddress) {
|
|
|
|
bool found = false;
|
|
|
|
char tagBuff[1024];
|
2011-12-15 12:31:41 +00:00
|
|
|
SharedLibraryInfo& shlibInfo = profile->getSharedLibraryInfo();
|
2011-12-07 19:48:15 +00:00
|
|
|
unsigned long pc = (unsigned long)mLeafAddress;
|
|
|
|
// TODO Use binary sort (STL)
|
2011-12-15 12:31:41 +00:00
|
|
|
for (size_t i = 0; i < shlibInfo.GetSize(); i++) {
|
|
|
|
SharedLibrary &e = shlibInfo.GetEntry(i);
|
2011-12-07 19:48:15 +00:00
|
|
|
if (pc > e.GetStart() && pc < e.GetEnd()) {
|
|
|
|
if (e.GetName()) {
|
|
|
|
found = true;
|
|
|
|
snprintf(tagBuff, 1024, "l-%900s@%llu\n", e.GetName(), pc - e.GetStart());
|
|
|
|
tag += string(tagBuff);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found) {
|
|
|
|
snprintf(tagBuff, 1024, "l-???@%llu\n", pc);
|
|
|
|
tag += string(tagBuff);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return tag;
|
|
|
|
}
|
2011-08-27 00:05:37 +00:00
|
|
|
|
|
|
|
#define PROFILE_DEFAULT_ENTRY 100000
|
2011-12-07 19:48:15 +00:00
|
|
|
#define PROFILE_DEFAULT_INTERVAL 10
|
|
|
|
|
2011-08-27 00:05:37 +00:00
|
|
|
void mozilla_sampler_init()
|
|
|
|
{
|
|
|
|
// TODO linux port: Use TLS with ifdefs
|
2011-12-04 19:09:00 +00:00
|
|
|
if (!mozilla::tls::create(&pkey_stack) ||
|
|
|
|
!mozilla::tls::create(&pkey_ticker)) {
|
2011-08-27 00:05:37 +00:00
|
|
|
LOG("Failed to init.");
|
|
|
|
return;
|
|
|
|
}
|
2011-12-08 15:46:02 +00:00
|
|
|
stack_key_initialized = true;
|
2011-08-27 00:05:37 +00:00
|
|
|
|
2011-12-07 19:48:15 +00:00
|
|
|
Stack *stack = new Stack();
|
2011-12-04 19:09:00 +00:00
|
|
|
mozilla::tls::set(pkey_stack, stack);
|
2011-12-07 19:48:15 +00:00
|
|
|
|
|
|
|
// We can't open pref so we use an environment variable
|
|
|
|
// to know if we should trigger the profiler on startup
|
|
|
|
// NOTE: Default
|
|
|
|
const char *val = PR_GetEnv("MOZ_PROFILER_STARTUP");
|
2011-12-01 22:40:33 +00:00
|
|
|
if (!val || !*val) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-12-07 19:48:15 +00:00
|
|
|
mozilla_sampler_start(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void mozilla_sampler_deinit()
|
|
|
|
{
|
|
|
|
mozilla_sampler_stop();
|
|
|
|
// We can't delete the Stack because we can be between a
|
|
|
|
// sampler call_enter/call_exit point.
|
|
|
|
// TODO Need to find a safe time to delete Stack
|
|
|
|
}
|
|
|
|
|
|
|
|
void mozilla_sampler_save() {
|
2011-12-04 19:09:00 +00:00
|
|
|
TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
|
2011-12-07 19:48:15 +00:00
|
|
|
if (!t) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
t->RequestSave();
|
|
|
|
// We're on the main thread already so we don't
|
|
|
|
// have to wait to handle the save request.
|
|
|
|
t->HandleSaveRequest();
|
|
|
|
}
|
|
|
|
|
|
|
|
char* mozilla_sampler_get_profile() {
|
2011-12-04 19:09:00 +00:00
|
|
|
TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
|
2011-12-07 19:48:15 +00:00
|
|
|
if (!t) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-12-16 14:03:54 +00:00
|
|
|
StringBuilder profile;
|
|
|
|
t->GetProfile()->ToString(profile);
|
2011-12-07 19:48:15 +00:00
|
|
|
|
2011-12-16 14:03:54 +00:00
|
|
|
char *rtn = (char*)malloc( (profile.Length()+1) * sizeof(char) );
|
|
|
|
strcpy(rtn, profile.Buffer());
|
2011-12-07 19:48:15 +00:00
|
|
|
return rtn;
|
|
|
|
}
|
2011-08-27 00:05:37 +00:00
|
|
|
|
2011-12-07 19:48:15 +00:00
|
|
|
// Values are only honored on the first start
|
|
|
|
void mozilla_sampler_start(int aProfileEntries, int aInterval)
|
|
|
|
{
|
2011-12-04 19:09:00 +00:00
|
|
|
Stack *stack = mozilla::tls::get<Stack>(pkey_stack);
|
2011-12-07 19:48:15 +00:00
|
|
|
if (!stack) {
|
|
|
|
ASSERT(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mozilla_sampler_stop();
|
|
|
|
|
|
|
|
TableTicker *t = new TableTicker(aInterval, aProfileEntries, stack);
|
2011-12-04 19:09:00 +00:00
|
|
|
mozilla::tls::set(pkey_ticker, t);
|
2011-08-27 00:05:37 +00:00
|
|
|
t->Start();
|
|
|
|
}
|
|
|
|
|
2011-12-07 19:48:15 +00:00
|
|
|
void mozilla_sampler_stop()
|
2011-08-27 00:05:37 +00:00
|
|
|
{
|
2011-12-04 19:09:00 +00:00
|
|
|
TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
|
2011-08-27 00:05:37 +00:00
|
|
|
if (!t) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
t->Stop();
|
2011-12-04 19:09:00 +00:00
|
|
|
mozilla::tls::set(pkey_ticker, (Stack*)NULL);
|
2011-12-07 19:48:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool mozilla_sampler_is_active()
|
|
|
|
{
|
2011-12-04 19:09:00 +00:00
|
|
|
TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
|
2011-12-07 19:48:15 +00:00
|
|
|
if (!t) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return t->IsActive();
|
|
|
|
}
|
|
|
|
|
2012-01-12 16:50:43 +00:00
|
|
|
double sResponsivenessTimes[100];
|
|
|
|
double sCurrResponsiveness = 0.f;
|
2011-12-07 19:48:15 +00:00
|
|
|
unsigned int sResponsivenessLoc = 0;
|
|
|
|
void mozilla_sampler_responsiveness(TimeStamp aTime)
|
|
|
|
{
|
|
|
|
if (!sLastTracerEvent.IsNull()) {
|
|
|
|
if (sResponsivenessLoc == 100) {
|
|
|
|
for(size_t i = 0; i < 100-1; i++) {
|
|
|
|
sResponsivenessTimes[i] = sResponsivenessTimes[i+1];
|
|
|
|
}
|
|
|
|
sResponsivenessLoc--;
|
|
|
|
//for(size_t i = 0; i < 100; i++) {
|
|
|
|
// sResponsivenessTimes[i] = 0;
|
|
|
|
//}
|
|
|
|
//sResponsivenessLoc = 0;
|
|
|
|
}
|
|
|
|
TimeDuration delta = aTime - sLastTracerEvent;
|
|
|
|
sResponsivenessTimes[sResponsivenessLoc++] = delta.ToMilliseconds();
|
|
|
|
}
|
|
|
|
|
|
|
|
sLastTracerEvent = aTime;
|
|
|
|
}
|
|
|
|
|
2012-01-12 16:50:43 +00:00
|
|
|
const double* mozilla_sampler_get_responsiveness()
|
2011-12-07 19:48:15 +00:00
|
|
|
{
|
|
|
|
return sResponsivenessTimes;
|
2011-08-27 00:05:37 +00:00
|
|
|
}
|
|
|
|
|