2008-04-16 Kevin McCullough <kmccullough@apple.com>
authorkmccullough@apple.com <kmccullough@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 16 Apr 2008 19:08:03 +0000 (19:08 +0000)
committerkmccullough@apple.com <kmccullough@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 16 Apr 2008 19:08:03 +0000 (19:08 +0000)
        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: https://svn.webkit.org/repository/webkit/trunk@31949 268f45cc-cd09-0410-ab3c-d52691b4dbfc

14 files changed:
JavaScriptCore/ChangeLog
JavaScriptCore/GNUmakefile.am
JavaScriptCore/JavaScriptCore.pri
JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj
JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
JavaScriptCore/JavaScriptCoreSources.bkl
JavaScriptCore/kjs/config.h
JavaScriptCore/kjs/function.cpp
JavaScriptCore/kjs/interpreter.cpp
JavaScriptCore/kjs/object.cpp
JavaScriptCore/profiler/FunctionCallProfile.cpp [new file with mode: 0644]
JavaScriptCore/profiler/FunctionCallProfile.h [new file with mode: 0644]
JavaScriptCore/profiler/Profiler.cpp [new file with mode: 0644]
JavaScriptCore/profiler/Profiler.h [new file with mode: 0644]

index 517b153..443a679 100644 (file)
@@ -1,3 +1,51 @@
+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.
index 2dd60bd..2ce805b 100644 (file)
@@ -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 \
index 5139c20..662d269 100644 (file)
@@ -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 += \
index 948fd6c..bdd6be9 100644 (file)
                                >\r
                        </File>\r
                </Filter>\r
+               <Filter\r
+                       Name="Profiler"\r
+                       >\r
+                       <File\r
+                               RelativePath="..\..\profiler\FunctionCallProfile.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\profiler\FunctionCallProfile.h"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\profiler\Profiler.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\profiler\Profiler.h"\r
+                               >\r
+                       </File>\r
+               </Filter>\r
        </Files>\r
        <Globals>\r
        </Globals>\r
index 519c53b..c3ef850 100644 (file)
                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, ); }; };
                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>"; };
                                F5C290E60284F98E018635CA /* JavaScriptCorePrefix.h */,
                                65162EF108E6A21C007556CD /* wtf */,
                                65417203039E01F90058BFEB /* pcre */,
+                               95AB831A0DA42C6900BC83F3 /* profiler */,
                                65417200039E01BA0058BFEB /* kjs */,
                                1432EBD70A34CAD400717B9F /* API */,
                                141211000A48772600480255 /* tests */,
                        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 = (
                                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 */,
                        );
                                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 */,
                        );
index d582189..10b1661 100644 (file)
@@ -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
index 99d16f7..f1f9c18 100644 (file)
@@ -85,3 +85,5 @@
 #if !PLATFORM(QT) && !PLATFORM(WX)
 #include <wtf/DisallowCType.h>
 #endif
+
+#define JAVASCRIPT_PROFILING 0
index 40a46b6..aa198a7 100644 (file)
@@ -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;
index 04a58c0..e0e1f18 100644 (file)
@@ -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) {
index ff63e71..11eca3a 100644 (file)
@@ -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;
diff --git a/JavaScriptCore/profiler/FunctionCallProfile.cpp b/JavaScriptCore/profiler/FunctionCallProfile.cpp
new file mode 100644 (file)
index 0000000..cc594ec
--- /dev/null
@@ -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<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
diff --git a/JavaScriptCore/profiler/FunctionCallProfile.h b/JavaScriptCore/profiler/FunctionCallProfile.h
new file mode 100644 (file)
index 0000000..e95cfa7
--- /dev/null
@@ -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 <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
diff --git a/JavaScriptCore/profiler/Profiler.cpp b/JavaScriptCore/profiler/Profiler.cpp
new file mode 100644 (file)
index 0000000..48d09d7
--- /dev/null
@@ -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 <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));
+}
+
+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 (file)
index 0000000..ae7e342
--- /dev/null
@@ -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 <wtf/OwnPtr.h>
+
+namespace KJS {
+
+    class ExecState;
+    class FunctionImp;
+    class JSObject;
+
+    class Profiler {
+        typedef Vector<UString>::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<UString>&, ExecState*) const;
+        void getStackNames(Vector<UString>&, ExecState*, JSObject*) const;
+        void getStackNames(Vector<UString>&, ExecState*, const UString& sourceURL, int startingLineNumber) const;
+
+        void insertStackNamesInTree(const Vector<UString>& 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<FunctionCallProfile> m_callTree;
+    };
+
+} // namespace KJS
+
+#endif // Profiler_h