diff --git a/JavaScriptCore/ChangeLog b/JavaScriptCore/ChangeLog index 517b153979cd2327c302c35617f2bd523569e9f7..443a67986c635541764f6e91e00d9bc3cf13b973 100644 --- a/JavaScriptCore/ChangeLog +++ b/JavaScriptCore/ChangeLog @@ -1,3 +1,51 @@ +2008-04-16 Kevin McCullough + + Reviewed by Sam and Geoff. + + - 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 Reviewed by Darin Adler. diff --git a/JavaScriptCore/GNUmakefile.am b/JavaScriptCore/GNUmakefile.am index 2dd60bd52128949cf09e6f3626626daf81120b8e..2ce805b52079cac25c95d749275fb023a2811b4b 100644 --- a/JavaScriptCore/GNUmakefile.am +++ b/JavaScriptCore/GNUmakefile.am @@ -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 \ diff --git a/JavaScriptCore/JavaScriptCore.pri b/JavaScriptCore/JavaScriptCore.pri index 5139c20f6b3140f5b012adda9e97c302eeac2d68..662d26965780724445e6868a373d9331d0dd930b 100644 --- a/JavaScriptCore/JavaScriptCore.pri +++ b/JavaScriptCore/JavaScriptCore.pri @@ -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 += \ diff --git a/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj b/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj index 948fd6c5bf96bd5affb9423812b5793c48490e50..bdd6be9115550dc5afff16b14c101ab5e8730e8b 100644 --- a/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj +++ b/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj @@ -882,6 +882,26 @@ > + + + + + + + + + + diff --git a/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj index 519c53bb0a609033593f471f285fbeaad5557700..c3ef850ad5abc08a9db923321ac54c91f685beaa 100644 --- a/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj +++ b/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj @@ -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 = ""; }; 93F0B3AA09BB4DC00068FCE3 /* Parser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Parser.h; sourceTree = ""; }; 93F1981A08245AAE001E9ABC /* keywords.table */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = text; path = keywords.table; sourceTree = ""; tabWidth = 8; }; + 95AB832E0DA42CAD00BC83F3 /* Profiler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Profiler.cpp; path = profiler/Profiler.cpp; sourceTree = ""; }; + 95AB832F0DA42CAD00BC83F3 /* Profiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Profiler.h; path = profiler/Profiler.h; sourceTree = ""; }; + 95AB83540DA43B4400BC83F3 /* FunctionCallProfile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FunctionCallProfile.cpp; path = profiler/FunctionCallProfile.cpp; sourceTree = ""; }; + 95AB83550DA43B4400BC83F3 /* FunctionCallProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FunctionCallProfile.h; path = profiler/FunctionCallProfile.h; sourceTree = ""; }; 95C18D3E0C90E7EF00E72F73 /* JSRetainPtr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSRetainPtr.h; sourceTree = ""; }; A785E3030D9341AB00953772 /* ExecStateInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExecStateInlines.h; sourceTree = ""; }; A8E894310CD0602400367179 /* JSCallbackObjectFunctions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSCallbackObjectFunctions.h; sourceTree = ""; }; @@ -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 = ""; + }; 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 */, ); diff --git a/JavaScriptCore/JavaScriptCoreSources.bkl b/JavaScriptCore/JavaScriptCoreSources.bkl index d582189f1a1d2672d9933c4a3c01a8501c42f1c1..10b1661811665ac2f94f1b2dac2546c10f8f80ee 100644 --- a/JavaScriptCore/JavaScriptCoreSources.bkl +++ b/JavaScriptCore/JavaScriptCoreSources.bkl @@ -94,7 +94,10 @@ Source files for JSCore. pcre/pcre_ucp_searchfuncs.cpp pcre/pcre_xclass.cpp - + + profiler/FunctionCallProfile.cpp + profiler/Profiler.cpp + wtf/Assertions.cpp wtf/FastMalloc.cpp diff --git a/JavaScriptCore/kjs/config.h b/JavaScriptCore/kjs/config.h index 99d16f70fbd88db53fe9ed1e620b2434a606be22..f1f9c18ab86b7ffd9a3ecdb01fedad5156eb8977 100644 --- a/JavaScriptCore/kjs/config.h +++ b/JavaScriptCore/kjs/config.h @@ -85,3 +85,5 @@ #if !PLATFORM(QT) && !PLATFORM(WX) #include #endif + +#define JAVASCRIPT_PROFILING 0 diff --git a/JavaScriptCore/kjs/function.cpp b/JavaScriptCore/kjs/function.cpp index 40a46b68062ee49a3b98abc3701469ece3749459..aa198a7c87d02fed261ecd57a4fe694ac419fe2c 100644 --- a/JavaScriptCore/kjs/function.cpp +++ b/JavaScriptCore/kjs/function.cpp @@ -41,6 +41,7 @@ #include "scope_chain_mark.h" #include "ExecStateInlines.h" #include +#include #include #include #include @@ -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 = parser().parse(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; diff --git a/JavaScriptCore/kjs/interpreter.cpp b/JavaScriptCore/kjs/interpreter.cpp index 04a58c0fbb3aaad5fa958e725d934edeae60d7ce..e0e1f18084025d3dd9d17f025204a0cba83e0cff 100644 --- a/JavaScriptCore/kjs/interpreter.cpp +++ b/JavaScriptCore/kjs/interpreter.cpp @@ -27,6 +27,7 @@ #include "JSGlobalObject.h" #include "Parser.h" #include "debugger.h" +#include #include #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 progNode = parser().parse(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) { diff --git a/JavaScriptCore/kjs/object.cpp b/JavaScriptCore/kjs/object.cpp index ff63e7107f0edae71bc3b3c6bea86cd7a74ecc5a..11eca3af90e2095a20dfda36fa6caeb878615d55 100644 --- a/JavaScriptCore/kjs/object.cpp +++ b/JavaScriptCore/kjs/object.cpp @@ -33,6 +33,7 @@ #include "operations.h" #include "PropertyNameArray.h" #include +#include #include // 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; diff --git a/JavaScriptCore/profiler/FunctionCallProfile.cpp b/JavaScriptCore/profiler/FunctionCallProfile.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cc594ece56d46b9fa4cbe3bd1a6b455b2046172b --- /dev/null +++ b/JavaScriptCore/profiler/FunctionCallProfile.cpp @@ -0,0 +1,131 @@ +/* + * 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 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 diff --git a/JavaScriptCore/profiler/FunctionCallProfile.h b/JavaScriptCore/profiler/FunctionCallProfile.h new file mode 100644 index 0000000000000000000000000000000000000000..e95cfa7546bebba43245d94816a4aaf9d27f1b0a --- /dev/null +++ b/JavaScriptCore/profiler/FunctionCallProfile.h @@ -0,0 +1,66 @@ +/* + * 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 + +namespace KJS { + + class FunctionCallProfile; + + typedef Vector::const_iterator StackIterator; + + class FunctionCallProfile { + public: + FunctionCallProfile(const UString& name); + ~FunctionCallProfile(); + + void willExecute(); + void didExecute(Vector 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 m_children; + }; + +} // namespace KJS + +#endif // FunctionCallProfile_h diff --git a/JavaScriptCore/profiler/Profiler.cpp b/JavaScriptCore/profiler/Profiler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..48d09d786a6b1650cbdfe974afe7bd304393435c --- /dev/null +++ b/JavaScriptCore/profiler/Profiler.cpp @@ -0,0 +1,175 @@ +/* + * 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 +#include + +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 callStackNames; + getStackNames(callStackNames, exec, calledFunction); + insertStackNamesInTree(callStackNames); +} + +void Profiler::willExecute(ExecState* exec, const UString& sourceURL, int startingLineNumber) +{ + ASSERT(m_profiling); + + Vector callStackNames; + getStackNames(callStackNames, exec, sourceURL, startingLineNumber); + insertStackNamesInTree(callStackNames); +} + +void Profiler::didExecute(ExecState* exec, JSObject* calledFunction) +{ + ASSERT(m_profiling); + + Vector callStackNames; + getStackNames(callStackNames, exec, calledFunction); + m_callTree->didExecute(callStackNames, 0); +} + +void Profiler::didExecute(ExecState* exec, const UString& sourceURL, int startingLineNumber) +{ + ASSERT(m_profiling); + + Vector callStackNames; + getStackNames(callStackNames, exec, sourceURL, startingLineNumber); + m_callTree->didExecute(callStackNames, 0); +} + +void Profiler::insertStackNamesInTree(const Vector& 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& 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& names, ExecState* exec, JSObject* calledFunction) const +{ + getStackNames(names, exec); + + if (calledFunction->inherits(&FunctionImp::info)) + names.append(getFunctionName(static_cast(calledFunction))); + else if (calledFunction->inherits(&InternalFunctionImp::info)) + names.append(static_cast(calledFunction)->functionName().ustring()); +} + + +void Profiler::getStackNames(Vector& names, ExecState* exec, const UString& sourceURL, int startingLineNumber) const +{ + getStackNames(names, exec); + names.append(Script + sourceURL + ": " + UString::from(startingLineNumber + 1)); +} + +UString Profiler::getFunctionName(FunctionImp* functionImp) const +{ + UString name = functionImp->functionName().ustring(); + int lineNumber = functionImp->body->lineNo(); + UString URL = functionImp->body->sourceURL(); + + return (name.isEmpty() ? "[anonymous function]" : name) + " " + URL + ": " + UString::from(lineNumber); +} + +void Profiler::printDataSampleStyle() const +{ + printf("Call graph:\n"); + m_callTree->printDataSampleStyle(0); + + // FIXME: Since no one seems to understand this part of sample's output I will implement it when I have a better idea of what it's meant to be doing. + printf("\nTotal number in stack (recursive counted multiple, when >=5):\n"); + printf("\nSort by top of stack, same collapsed (when >= 5):\n"); +} + +void Profiler::debugLog(UString message) +{ + printf("Profiler Log: %s\n", message.UTF8String().c_str()); +} + +} // namespace KJS diff --git a/JavaScriptCore/profiler/Profiler.h b/JavaScriptCore/profiler/Profiler.h new file mode 100644 index 0000000000000000000000000000000000000000..ae7e3420d176aeb3338821c9c6081f713cffdd26 --- /dev/null +++ b/JavaScriptCore/profiler/Profiler.h @@ -0,0 +1,79 @@ +/* + * 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 Profiler_h +#define Profiler_h + +#include "FunctionCallProfile.h" +#include + +namespace KJS { + + class ExecState; + class FunctionImp; + class JSObject; + + class Profiler { + typedef Vector::const_iterator NameIterator; + + public: + static Profiler* profiler(); + static void debugLog(UString); + + void startProfiling(); + void stopProfiling(); + void willExecute(ExecState*, JSObject* calledFunction); + void willExecute(ExecState*, const UString& sourceURL, int startingLineNumber); + void didExecute(ExecState*, JSObject* calledFunction); + void didExecute(ExecState*, const UString& sourceURL, int startingLineNumber); + + void printDataSampleStyle() const; + + private: + Profiler() + : m_profiling(false) + { + } + + void getStackNames(Vector&, ExecState*) const; + void getStackNames(Vector&, ExecState*, JSObject*) const; + void getStackNames(Vector&, ExecState*, const UString& sourceURL, int startingLineNumber) const; + + void insertStackNamesInTree(const Vector& callStackNames); + + UString getFunctionName(FunctionImp*) const; + + bool m_profiling; + // FIXME: Make this a vector of FunctionCallProfiles where each one is the + // root of a new thread. + OwnPtr m_callTree; + }; + +} // namespace KJS + +#endif // Profiler_h