Commit ac769767 authored by kmccullough@apple.com's avatar kmccullough@apple.com

2008-04-16 Kevin McCullough <kmccullough@apple.com>

        Reviewed by Sam and Geoff.

        -<rdar://problem/5770054> JavaScript profiler (10928)
        Inital profiler prototype

        * GNUmakefile.am: Added new files to project
        * JavaScriptCore.pri: Ditto
        * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj: Ditto
        * JavaScriptCore.xcodeproj/project.pbxproj: Ditto
        * JavaScriptCoreSources.bkl: Ditto
        * kjs/config.h: Put compiling flag in here.
        * kjs/function.cpp: Instrument calling the function eval().
        (KJS::eval):
        * kjs/interpreter.cpp: Instrument evaluating global scopes.
        (KJS::Interpreter::evaluate):
        * kjs/object.cpp: Instrument JS function calls.
        (KJS::JSObject::call):
        * profiler: Added.
        * profiler/FunctionCallProfile.cpp: Added.
        (KJS::FunctionCallProfile::FunctionCallProfile):
        (KJS::FunctionCallProfile::~FunctionCallProfile):
        (KJS::FunctionCallProfile::willExecute): Call right before the JS function or executing context is executed to start the profiler's timer.
        (KJS::FunctionCallProfile::didExecute): Call right after the JS function or executing context is executed to stop the profiler's timer.
        (KJS::FunctionCallProfile::addChild): Add a child to the current FunctionCallProfile if it isn't already a child of the current FunctionalCallProfile.
        (KJS::FunctionCallProfile::findChild): Return the child that matches the given name if there is one.
        (KJS::FunctionCallProfile::printDataSampleStyle): Print the current profiled information in a format that matches sample's output.
        * profiler/FunctionCallProfile.h: Added.
        (KJS::FunctionCallProfile::FunctionCallProfile):
        (KJS::FunctionCallProfile::~FunctionCallProfile):
        (KJS::FunctionCallProfile::functionName):
        (KJS::FunctionCallProfile::microSecs):
        * profiler/Profiler.cpp: Added.
        (KJS::Profiler::profiler):
        (KJS::Profiler::sharedProfiler): Return global singleton (may change due to multi-threading concerns)
        (KJS::Profiler::startProfiling): Don't start collecting profiling information until the user starts the profiler. Also don't clear old prfiled data until the profiler is restarted.
        (KJS::Profiler::stopProfiling): Stop collecting profile information.
        (KJS::Profiler::willExecute): Same as above. 
        (KJS::Profiler::didExecute): Same as above.
        (KJS::Profiler::insertStackNamesInTree): Follow the stack of the given names and if a sub-stack is not in the current tree, add it.
        (KJS::Profiler::getStackNames): Get the names from the different passed in parameters and order them as a stack.
        (KJS::Profiler::getFunctionName): Get the function name from the given parameter.
        (KJS::Profiler::printDataSampleStyle): Print the current profiled information in a format that matches sample's output.
        (KJS::Profiler::debugLog):
        * profiler/Profiler.h: Added.
        (KJS::Profiler::Profiler):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@31949 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 843df6f3
2008-04-16 Kevin McCullough <kmccullough@apple.com>
Reviewed by Sam and Geoff.
-<rdar://problem/5770054> JavaScript profiler (10928)
Inital profiler prototype
* GNUmakefile.am: Added new files to project
* JavaScriptCore.pri: Ditto
* JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj: Ditto
* JavaScriptCore.xcodeproj/project.pbxproj: Ditto
* JavaScriptCoreSources.bkl: Ditto
* kjs/config.h: Put compiling flag in here.
* kjs/function.cpp: Instrument calling the function eval().
(KJS::eval):
* kjs/interpreter.cpp: Instrument evaluating global scopes.
(KJS::Interpreter::evaluate):
* kjs/object.cpp: Instrument JS function calls.
(KJS::JSObject::call):
* profiler: Added.
* profiler/FunctionCallProfile.cpp: Added.
(KJS::FunctionCallProfile::FunctionCallProfile):
(KJS::FunctionCallProfile::~FunctionCallProfile):
(KJS::FunctionCallProfile::willExecute): Call right before the JS function or executing context is executed to start the profiler's timer.
(KJS::FunctionCallProfile::didExecute): Call right after the JS function or executing context is executed to stop the profiler's timer.
(KJS::FunctionCallProfile::addChild): Add a child to the current FunctionCallProfile if it isn't already a child of the current FunctionalCallProfile.
(KJS::FunctionCallProfile::findChild): Return the child that matches the given name if there is one.
(KJS::FunctionCallProfile::printDataSampleStyle): Print the current profiled information in a format that matches sample's output.
* profiler/FunctionCallProfile.h: Added.
(KJS::FunctionCallProfile::FunctionCallProfile):
(KJS::FunctionCallProfile::~FunctionCallProfile):
(KJS::FunctionCallProfile::functionName):
(KJS::FunctionCallProfile::microSecs):
* profiler/Profiler.cpp: Added.
(KJS::Profiler::profiler):
(KJS::Profiler::sharedProfiler): Return global singleton (may change due to multi-threading concerns)
(KJS::Profiler::startProfiling): Don't start collecting profiling information until the user starts the profiler. Also don't clear old prfiled data until the profiler is restarted.
(KJS::Profiler::stopProfiling): Stop collecting profile information.
(KJS::Profiler::willExecute): Same as above.
(KJS::Profiler::didExecute): Same as above.
(KJS::Profiler::insertStackNamesInTree): Follow the stack of the given names and if a sub-stack is not in the current tree, add it.
(KJS::Profiler::getStackNames): Get the names from the different passed in parameters and order them as a stack.
(KJS::Profiler::getFunctionName): Get the function name from the given parameter.
(KJS::Profiler::printDataSampleStyle): Print the current profiled information in a format that matches sample's output.
(KJS::Profiler::debugLog):
* profiler/Profiler.h: Added.
(KJS::Profiler::Profiler):
2008-04-16 Sam Weinig <sam@webkit.org>
Reviewed by Darin Adler.
......
......@@ -47,6 +47,8 @@ javascriptcore_sources += \
JavaScriptCore/pcre/pcre_tables.cpp \
JavaScriptCore/pcre/pcre_ucp_searchfuncs.cpp \
JavaScriptCore/pcre/pcre_xclass.cpp \
JavaScriptCore/profiler/FunctionCallProfile.cpp \
JavaScriptCore/profiler/Profiler.cpp \
JavaScriptCore/wtf/Assertions.cpp \
JavaScriptCore/wtf/HashTable.cpp \
JavaScriptCore/wtf/MainThread.cpp \
......
......@@ -98,6 +98,8 @@ SOURCES += \
kjs/string_object.cpp \
kjs/ustring.cpp \
kjs/value.cpp \
profiler/FunctionCallProfile.cpp \
profiler/Profiler.cpp \
wtf/FastMalloc.cpp
!qt-port:SOURCES += \
......
......@@ -882,6 +882,26 @@
>
</File>
</Filter>
<Filter
Name="Profiler"
>
<File
RelativePath="..\..\profiler\FunctionCallProfile.cpp"
>
</File>
<File
RelativePath="..\..\profiler\FunctionCallProfile.h"
>
</File>
<File
RelativePath="..\..\profiler\Profiler.cpp"
>
</File>
<File
RelativePath="..\..\profiler\Profiler.h"
>
</File>
</Filter>
</Files>
<Globals>
</Globals>
......
......@@ -186,6 +186,10 @@
93E26BE608B1517100F85226 /* pcre_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 93E26BE508B1517100F85226 /* pcre_internal.h */; };
93E26BFE08B151D400F85226 /* ucpinternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 93E26BFC08B151D400F85226 /* ucpinternal.h */; };
93F0B3AC09BB4DC00068FCE3 /* Parser.h in Headers */ = {isa = PBXBuildFile; fileRef = 93F0B3AA09BB4DC00068FCE3 /* Parser.h */; settings = {ATTRIBUTES = (Private, ); }; };
95AB83420DA4322500BC83F3 /* Profiler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95AB832E0DA42CAD00BC83F3 /* Profiler.cpp */; };
95AB83480DA432EB00BC83F3 /* Profiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 95AB832F0DA42CAD00BC83F3 /* Profiler.h */; };
95AB83560DA43C3000BC83F3 /* FunctionCallProfile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 95AB83540DA43B4400BC83F3 /* FunctionCallProfile.cpp */; };
95AB83570DA43C3000BC83F3 /* FunctionCallProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 95AB83550DA43B4400BC83F3 /* FunctionCallProfile.h */; };
95C18D490C90E82600E72F73 /* JSRetainPtr.h in Headers */ = {isa = PBXBuildFile; fileRef = 95C18D3E0C90E7EF00E72F73 /* JSRetainPtr.h */; settings = {ATTRIBUTES = (Private, ); }; };
A8E894320CD0602400367179 /* JSCallbackObjectFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = A8E894310CD0602400367179 /* JSCallbackObjectFunctions.h */; };
A8E894340CD0603F00367179 /* JSGlobalObject.h in Headers */ = {isa = PBXBuildFile; fileRef = A8E894330CD0603F00367179 /* JSGlobalObject.h */; settings = {ATTRIBUTES = (Private, ); }; };
......@@ -490,6 +494,10 @@
93F0B3A909BB4DC00068FCE3 /* Parser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Parser.cpp; sourceTree = "<group>"; };
93F0B3AA09BB4DC00068FCE3 /* Parser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Parser.h; sourceTree = "<group>"; };
93F1981A08245AAE001E9ABC /* keywords.table */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = text; path = keywords.table; sourceTree = "<group>"; tabWidth = 8; };
95AB832E0DA42CAD00BC83F3 /* Profiler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Profiler.cpp; path = profiler/Profiler.cpp; sourceTree = "<group>"; };
95AB832F0DA42CAD00BC83F3 /* Profiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Profiler.h; path = profiler/Profiler.h; sourceTree = "<group>"; };
95AB83540DA43B4400BC83F3 /* FunctionCallProfile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FunctionCallProfile.cpp; path = profiler/FunctionCallProfile.cpp; sourceTree = "<group>"; };
95AB83550DA43B4400BC83F3 /* FunctionCallProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FunctionCallProfile.h; path = profiler/FunctionCallProfile.h; sourceTree = "<group>"; };
95C18D3E0C90E7EF00E72F73 /* JSRetainPtr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSRetainPtr.h; sourceTree = "<group>"; };
A785E3030D9341AB00953772 /* ExecStateInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExecStateInlines.h; sourceTree = "<group>"; };
A8E894310CD0602400367179 /* JSCallbackObjectFunctions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSCallbackObjectFunctions.h; sourceTree = "<group>"; };
......@@ -637,6 +645,7 @@
F5C290E60284F98E018635CA /* JavaScriptCorePrefix.h */,
65162EF108E6A21C007556CD /* wtf */,
65417203039E01F90058BFEB /* pcre */,
95AB831A0DA42C6900BC83F3 /* profiler */,
65417200039E01BA0058BFEB /* kjs */,
1432EBD70A34CAD400717B9F /* API */,
141211000A48772600480255 /* tests */,
......@@ -957,6 +966,17 @@
tabWidth = 4;
usesTabs = 0;
};
95AB831A0DA42C6900BC83F3 /* profiler */ = {
isa = PBXGroup;
children = (
95AB83540DA43B4400BC83F3 /* FunctionCallProfile.cpp */,
95AB83550DA43B4400BC83F3 /* FunctionCallProfile.h */,
95AB832E0DA42CAD00BC83F3 /* Profiler.cpp */,
95AB832F0DA42CAD00BC83F3 /* Profiler.h */,
);
name = profiler;
sourceTree = "<group>";
};
E195678D09E7CF1200B89D13 /* unicode */ = {
isa = PBXGroup;
children = (
......@@ -1120,6 +1140,8 @@
E1EE79280D6C964500FEA3BA /* Locker.h in Headers */,
E1A862AB0D7EBB7D001EC6AA /* Collator.h in Headers */,
E17863400D9BEC0000D74E75 /* InitializeThreading.h in Headers */,
95AB83480DA432EB00BC83F3 /* Profiler.h in Headers */,
95AB83570DA43C3000BC83F3 /* FunctionCallProfile.h in Headers */,
E1B7C8BE0DA3A3360074B0DC /* ThreadSpecific.h in Headers */,
06D358B20DAADA93003B174E /* MainThread.h in Headers */,
);
......@@ -1375,6 +1397,8 @@
E1A862A90D7EBB76001EC6AA /* CollatorICU.cpp in Sources */,
E1A862D60D7F2B5C001EC6AA /* CollatorDefault.cpp in Sources */,
E178636D0D9BEEC300D74E75 /* InitializeThreading.cpp in Sources */,
95AB83420DA4322500BC83F3 /* Profiler.cpp in Sources */,
95AB83560DA43C3000BC83F3 /* FunctionCallProfile.cpp in Sources */,
06D358B30DAADAA4003B174E /* MainThread.cpp in Sources */,
06D358B40DAADAAA003B174E /* MainThreadMac.mm in Sources */,
);
......
......@@ -94,7 +94,10 @@ Source files for JSCore.
pcre/pcre_ucp_searchfuncs.cpp
pcre/pcre_xclass.cpp
</set>
<set append="1" var="PROFILER_SOURCES">
profiler/FunctionCallProfile.cpp
profiler/Profiler.cpp
</set>
<set append="1" var="WTF_SOURCES">
wtf/Assertions.cpp
wtf/FastMalloc.cpp
......
......@@ -85,3 +85,5 @@
#if !PLATFORM(QT) && !PLATFORM(WX)
#include <wtf/DisallowCType.h>
#endif
#define JAVASCRIPT_PROFILING 0
......@@ -41,6 +41,7 @@
#include "scope_chain_mark.h"
#include "ExecStateInlines.h"
#include <errno.h>
#include <profiler/Profiler.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
......@@ -717,6 +718,11 @@ JSValue* eval(ExecState* exec, const ScopeChain& scopeChain, JSVariableObject* v
int sourceId;
int errLine;
UString errMsg;
#if JAVASCRIPT_PROFILING
Profiler::profiler()->willExecute(exec, UString(), 0);
#endif
RefPtr<EvalNode> evalNode = parser().parse<EvalNode>(UString(), 0, s.data(), s.size(), &sourceId, &errLine, &errMsg);
Debugger* dbg = exec->dynamicGlobalObject()->debugger();
......@@ -733,6 +739,10 @@ JSValue* eval(ExecState* exec, const ScopeChain& scopeChain, JSVariableObject* v
JSValue* value = evalNode->execute(&newExec);
#if JAVASCRIPT_PROFILING
Profiler::profiler()->didExecute(exec, UString(), 0);
#endif
if (newExec.completionType() == Throw) {
exec->setException(value);
return value;
......
......@@ -27,6 +27,7 @@
#include "JSGlobalObject.h"
#include "Parser.h"
#include "debugger.h"
#include <profiler/Profiler.h>
#include <stdio.h>
#if !PLATFORM(WIN_OS)
......@@ -70,6 +71,11 @@ Completion Interpreter::evaluate(ExecState* exec, const UString& sourceURL, int
int sourceId;
int errLine;
UString errMsg;
#if JAVASCRIPT_PROFILING
Profiler::profiler()->willExecute(exec, sourceURL, startingLineNumber);
#endif
RefPtr<ProgramNode> progNode = parser().parse<ProgramNode>(sourceURL, startingLineNumber, code, codeLength, &sourceId, &errLine, &errMsg);
// notify debugger that source has been parsed
......@@ -103,7 +109,11 @@ Completion Interpreter::evaluate(ExecState* exec, const UString& sourceURL, int
JSValue* value = progNode->execute(&newExec);
res = Completion(newExec.completionType(), value);
}
#if JAVASCRIPT_PROFILING
Profiler::profiler()->didExecute(exec, sourceURL, startingLineNumber);
#endif
globalObject->decRecursion();
if (shouldPrintExceptions() && res.complType() == Throw) {
......
......@@ -33,6 +33,7 @@
#include "operations.h"
#include "PropertyNameArray.h"
#include <math.h>
#include <profiler/Profiler.h>
#include <wtf/Assertions.h>
// maximum global call stack size. Protects against accidental or
......@@ -93,7 +94,16 @@ JSValue *JSObject::call(ExecState *exec, JSObject *thisObj, const List &args)
}
#endif
JSValue* ret = callAsFunction(exec, thisObj, args);
#if JAVASCRIPT_PROFILING
Profiler::profiler()->willExecute(exec, this);
#endif
JSValue *ret = callAsFunction(exec,thisObj,args);
#if JAVASCRIPT_PROFILING
Profiler::profiler()->didExecute(exec, this);
#endif
#if KJS_MAX_STACK > 0
--depth;
......
/*
* Copyright (C) 2008 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "FunctionCallProfile.h"
#include "Profiler.h"
#include "DateMath.h"
namespace KJS {
FunctionCallProfile::FunctionCallProfile(const UString& name)
: m_functionName(name)
, m_timeSum(0)
{
m_startTime = getCurrentUTCTime();
}
FunctionCallProfile::~FunctionCallProfile()
{
deleteAllValues(m_children);
}
void FunctionCallProfile::willExecute()
{
m_startTime = getCurrentUTCTime();
}
void FunctionCallProfile::didExecute(Vector<UString> stackNames, unsigned int stackIndex)
{
if (stackIndex == stackNames.size()) {
ASSERT(stackNames[stackIndex - 1] == m_functionName);
m_timeSum += getCurrentUTCTime() - m_startTime;
ASSERT(m_timeSum > 0);
return;
}
for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end() && stackIndex < stackNames.size(); ++currentChild) {
if ((*currentChild)->functionName() == stackNames[stackIndex]) {
(*currentChild)->didExecute(stackNames, ++stackIndex);
return;
}
}
}
void FunctionCallProfile::addChild(FunctionCallProfile* child)
{
if (!child)
return;
for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild) {
if ((*currentChild)->functionName() == child->functionName())
return;
}
m_children.append(child);
}
FunctionCallProfile* FunctionCallProfile::findChild(const UString& name)
{
for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild) {
if ((*currentChild)->functionName() == name)
return *currentChild;
}
return 0;
}
// print the profiled data in a format that matches the tool sample's output.
double FunctionCallProfile::printDataSampleStyle(int indentLevel)
{
printf(" ");
// Print function names
if (indentLevel) {
for (int i = 0; i < indentLevel; ++i)
printf(" ");
// We've previously asserted that m_timeSum will always be >= 1
printf("%f %s\n", m_timeSum, m_functionName.UTF8String().c_str());
} else
printf("%s\n", m_functionName.UTF8String().c_str());
++indentLevel;
// Print children's names and information
double sumOfChildrensTimes = 0.0;
for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild)
sumOfChildrensTimes += (*currentChild)->printDataSampleStyle(indentLevel);
// Print remainder of time to match sample's output
if (sumOfChildrensTimes < m_timeSum) {
printf(" ");
while (indentLevel--)
printf(" ");
printf("%f %s\n", m_timeSum - sumOfChildrensTimes, m_functionName.UTF8String().c_str());
}
return m_timeSum;
}
} // namespace KJS
/*
* Copyright (C) 2008 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef FunctionCallProfile_h
#define FunctionCallProfile_h
#include <kjs/ustring.h>
namespace KJS {
class FunctionCallProfile;
typedef Vector<FunctionCallProfile*>::const_iterator StackIterator;
class FunctionCallProfile {
public:
FunctionCallProfile(const UString& name);
~FunctionCallProfile();
void willExecute();
void didExecute(Vector<UString> stackNames, unsigned int stackIndex);
void addChild(FunctionCallProfile* child);
FunctionCallProfile* findChild(const UString& name);
UString functionName() const { return m_functionName; }
double microSecs() const { return m_timeSum; }
double printDataSampleStyle(int indentLevel);
private:
UString m_functionName;
double m_timeSum;
double m_startTime;
Vector<FunctionCallProfile*> m_children;
};
} // namespace KJS
#endif // FunctionCallProfile_h
/*
* Copyright (C) 2008 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "Profiler.h"
#include "FunctionCallProfile.h"
#include <kjs/ExecState.h>
#include <kjs/function.h>
namespace KJS {
static Profiler* sharedProfiler = 0;
static const char* Script = "[SCRIPT] ";
Profiler* Profiler::profiler()
{
if (!sharedProfiler)
sharedProfiler = new Profiler;
return sharedProfiler;
}
void Profiler::startProfiling()
{
if (m_profiling)
return;
// FIXME: When multi-threading is supported this will be a vector and calls
// into the profiler will need to know which thread it is executing on.
m_callTree.set(new FunctionCallProfile("Thread_1"));
m_profiling = true;
}
void Profiler::stopProfiling()
{
m_profiling = false;
}
void Profiler::willExecute(ExecState* exec, JSObject* calledFunction)
{
ASSERT(m_profiling);
Vector<UString> callStackNames;
getStackNames(callStackNames, exec, calledFunction);
insertStackNamesInTree(callStackNames);
}
void Profiler::willExecute(ExecState* exec, const UString& sourceURL, int startingLineNumber)
{
ASSERT(m_profiling);
Vector<UString> callStackNames;
getStackNames(callStackNames, exec, sourceURL, startingLineNumber);
insertStackNamesInTree(callStackNames);
}
void Profiler::didExecute(ExecState* exec, JSObject* calledFunction)
{
ASSERT(m_profiling);
Vector<UString> callStackNames;
getStackNames(callStackNames, exec, calledFunction);
m_callTree->didExecute(callStackNames, 0);
}
void Profiler::didExecute(ExecState* exec, const UString& sourceURL, int startingLineNumber)
{
ASSERT(m_profiling);
Vector<UString> callStackNames;
getStackNames(callStackNames, exec, sourceURL, startingLineNumber);
m_callTree->didExecute(callStackNames, 0);
}
void Profiler::insertStackNamesInTree(const Vector<UString>& callStackNames)
{
FunctionCallProfile* callTreeInsertionPoint = 0;
FunctionCallProfile* foundNameInTree = m_callTree.get();
NameIterator callStackLocation = callStackNames.begin();
while (callStackLocation != callStackNames.end() && foundNameInTree) {
callTreeInsertionPoint = foundNameInTree;
foundNameInTree = callTreeInsertionPoint->findChild(*callStackLocation);
++callStackLocation;
}
if (!foundNameInTree) { // Insert remains of the stack into the call tree.
--callStackLocation;
for (FunctionCallProfile* next; callStackLocation != callStackNames.end(); ++callStackLocation) {
next = new FunctionCallProfile(*callStackLocation);
callTreeInsertionPoint->addChild(next);
callTreeInsertionPoint = next;
}
} else // We are calling a function that is already in the call tree.
foundNameInTree->willExecute();
}
void Profiler::getStackNames(Vector<UString>& names, ExecState* exec) const
{
UString currentName;
for (ExecState* currentState = exec; currentState; currentState = currentState->callingExecState()) {
if (FunctionImp* functionImp = exec->function())
names.prepend(getFunctionName(functionImp));
else if (ScopeNode* scopeNode = exec->scopeNode())
names.prepend(Script + scopeNode->sourceURL() + ": " + UString::from(scopeNode->lineNo() + 1)); // FIXME: Why is the line number always off by one?
}
}
void Profiler::getStackNames(Vector<UString>& names, ExecState* exec, JSObject* calledFunction) const
{
getStackNames(names, exec);
if (calledFunction->inherits(&FunctionImp::info))
names.append(getFunctionName(static_cast<FunctionImp*>(calledFunction)));
else if (calledFunction->inherits(&InternalFunctionImp::info))
names.append(static_cast<InternalFunctionImp*>(calledFunction)->functionName().ustring());
}
void Profiler::getStackNames(Vector<UString>& names, ExecState* exec, const UString& sourceURL, int startingLineNumber) const
{
getStackNames(names, exec);
names.append(Script + sourceURL + ": " + UString::from(startingLineNumber + 1));
}