2008-07-23 Gavin Barraclough <barraclough@apple.com>
authorbarraclough@apple.com <barraclough@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 23 Jul 2008 22:36:39 +0000 (22:36 +0000)
committerbarraclough@apple.com <barraclough@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 23 Jul 2008 22:36:39 +0000 (22:36 +0000)
        Reviewed by Geoff Garen.

        Sampling tool to analyze cost of instruction execution and identify hot regions of JS code.
        Enable Switches by setting SAMPLING_TOOL_ENABLED in Opcode.h.

        * JavaScriptCore.exp: Export symbols for Shell.cpp.
        * VM/Machine.cpp:     Added sampling hooks.
        * VM/Machine.h:       Machine contains a pointer to a sampler, when sampling.
        * VM/Opcode.cpp:      Tool implementation.
        * VM/Opcode.h:        Tool declaration.
        * kjs/Shell.cpp:      Initialize the sampler, if enabled.
        * kjs/nodes.cpp:      Added sampling hooks.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@35305 268f45cc-cd09-0410-ab3c-d52691b4dbfc

JavaScriptCore/ChangeLog
JavaScriptCore/JavaScriptCore.exp
JavaScriptCore/VM/Machine.cpp
JavaScriptCore/VM/Machine.h
JavaScriptCore/VM/Opcode.cpp
JavaScriptCore/VM/Opcode.h
JavaScriptCore/kjs/Shell.cpp
JavaScriptCore/kjs/nodes.cpp

index 17dd617..b95ec81 100644 (file)
@@ -1,3 +1,18 @@
+2008-07-23  Gavin Barraclough  <barraclough@apple.com>
+
+        Reviewed by Geoff Garen.
+
+        Sampling tool to analyze cost of instruction execution and identify hot regions of JS code.
+        Enable Switches by setting SAMPLING_TOOL_ENABLED in Opcode.h.
+
+        * JavaScriptCore.exp: Export symbols for Shell.cpp.
+        * VM/Machine.cpp:     Added sampling hooks.
+        * VM/Machine.h:       Machine contains a pointer to a sampler, when sampling.
+        * VM/Opcode.cpp:      Tool implementation.
+        * VM/Opcode.h:        Tool declaration.
+        * kjs/Shell.cpp:      Initialize the sampler, if enabled.
+        * kjs/nodes.cpp:      Added sampling hooks.
+
 2008-07-23  Gabor Loki  <loki@inf.u-szeged.hu>
 
         Bug 20097: [Qt] 20% Sunspider slow-down
index fe9ebc8..7ca4342 100644 (file)
@@ -322,3 +322,6 @@ __ZTVN3KJS16JSVariableObjectE
 __ZTVN3KJS8JSObjectE
 _jscore_fastmalloc_introspection
 _kJSClassDefinitionEmpty
+__ZN3KJS12SamplingTool4stopEv
+__ZN3KJS12SamplingTool5startEj
+__ZN3KJS12SamplingTool4dumpEPNS_9ExecStateE
index a1b5450..7d39f7b 100644 (file)
@@ -473,7 +473,8 @@ NEVER_INLINE JSValue* Machine::callEval(ExecState* exec, JSObject* thisObj, Scop
 }
 
 Machine::Machine()
-    : m_reentryDepth(0)
+    : m_sampler(0)
+    , m_reentryDepth(0)
     , m_timeoutTime(0)
     , m_timeAtLastCheckTimeout(0)
     , m_timeExecuting(0)
@@ -730,6 +731,8 @@ JSValue* Machine::execute(ProgramNode* programNode, ExecState* exec, ScopeChainN
     JSValue* result = privateExecute(Normal, &newExec, &m_registerFile, r, scopeChain, codeBlock, exception);
     m_reentryDepth--;
 
+    MACHINE_SAMPLING_privateExecuteReturned();
+
     if (*profiler) {
         (*profiler)->didExecute(exec, programNode->sourceURL(), programNode->lineNo());
         if (!m_reentryDepth)
@@ -791,6 +794,8 @@ JSValue* Machine::execute(FunctionBodyNode* functionBodyNode, ExecState* exec, J
     JSValue* result = privateExecute(Normal, &newExec, &m_registerFile, r, scopeChain, newCodeBlock, exception);
     m_reentryDepth--;
 
+    MACHINE_SAMPLING_privateExecuteReturned();
+
     if (*profiler && !m_reentryDepth)
         (*profiler)->didFinishAllExecution(exec);
 
@@ -857,6 +862,8 @@ JSValue* Machine::execute(EvalNode* evalNode, ExecState* exec, JSObject* thisObj
     JSValue* result = privateExecute(Normal, &newExec, &m_registerFile, r, scopeChain, codeBlock, exception);
     m_reentryDepth--;
 
+    MACHINE_SAMPLING_privateExecuteReturned();
+
     if (*profiler) {
         (*profiler)->didExecute(exec, evalNode->sourceURL(), evalNode->lineNo());
         if (!m_reentryDepth)
@@ -1028,7 +1035,7 @@ JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFi
     }
     
 #if HAVE(COMPUTED_GOTO)
-    #define NEXT_OPCODE goto *vPC->u.opcode
+    #define NEXT_OPCODE MACHINE_SAMPLING_sample(codeBlock, vPC); goto *vPC->u.opcode
 #if DUMP_OPCODE_STATS
     #define BEGIN_OPCODE(opcode) opcode: OpcodeStats::recordInstruction(opcode);
 #else
@@ -1036,7 +1043,7 @@ JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFi
 #endif
     NEXT_OPCODE;
 #else
-    #define NEXT_OPCODE continue
+    #define NEXT_OPCODE MACHINE_SAMPLING_sample(codeBlock, vPC); continue
 #if DUMP_OPCODE_STATS
     #define BEGIN_OPCODE(opcode) case opcode: OpcodeStats::recordInstruction(opcode);
 #else
@@ -2291,6 +2298,8 @@ JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFi
             JSValue* thisValue = thisVal == missingThisObjectMarker() ? exec->globalThisValue() : r[thisVal].jsValue(exec);
             ArgList args(r + firstArg + 1, argCount - 1);
 
+            MACHINE_SAMPLING_callingNativeFunction();
+
             JSValue* returnValue = callData.native.function(exec, static_cast<JSObject*>(v), thisValue, args);
             VM_CHECK_EXCEPTION();
 
index f19557c..d8f306f 100644 (file)
@@ -119,6 +119,8 @@ namespace KJS {
             m_timeoutTime = 0;
             m_timeoutCheckCount = 0;
         }
+        
+        SamplingTool* m_sampler;
 
     private:
         enum ExecutionFlag { Normal, InitializeAndReturn };
index 3d7b252..72f55bc 100644 (file)
 #include "Opcode.h"
 
 #include <stdlib.h>
+#include "CodeBlock.h"
+#include "Machine.h"
 
 using namespace std;
 
 namespace KJS {
 
-#if DUMP_OPCODE_STATS
-
-long long OpcodeStats::opcodeCounts[numOpcodeIDs];
-long long OpcodeStats::opcodePairCounts[numOpcodeIDs][numOpcodeIDs];
-int OpcodeStats::lastOpcode = -1;
-
-static OpcodeStats logger;
+#if SAMPLING_TOOL_ENABLED || DUMP_OPCODE_STATS
 
 static const char* opcodeNames[] = {
-    "load        ",
-    "new_object  ",
-    "new_array   ",
-    "new_regexp  ",
-    "mov         ",
+    "load             ",
+    "new_object       ",
+    "new_array        ",
+    "new_regexp       ",
+    "mov              ",
     
-    "not         ",
-    "eq          ",
-    "neq         ",
-    "stricteq    ",
-    "nstricteq   ",
-    "less        ",
-    "lesseq      ",
+    "not              ",
+    "eq               ",
+    "neq              ",
+    "stricteq         ",
+    "nstricteq        ",
+    "less             ",
+    "lesseq           ",
     
-    "pre_inc     ",
-    "pre_dec     ",
-    "post_inc    ",
-    "post_dec    ",
-    "to_jsnumber ",
-    "negate      ",
-    "add         ",
-    "mul         ",
-    "div         ",
-    "mod         ",
-    "sub         ",
+    "pre_inc          ",
+    "pre_dec          ",
+    "post_inc         ",
+    "post_dec         ",
+    "to_jsnumber      ",
+    "negate           ",
+    "add              ",
+    "mul              ",
+    "div              ",
+    "mod              ",
+    "sub              ",
     
-    "lshift      ",
-    "rshift      ",
-    "urshift     ",
-    "bitand      ",
-    "bitxor      ",
-    "bitor       ",
-    "bitnot      ",
+    "lshift           ",
+    "rshift           ",
+    "urshift          ",
+    "bitand           ",
+    "bitxor           ",
+    "bitor            ",
+    "bitnot           ",
     
-    "instanceof  ",
-    "typeof      ",
-    "in          ",
-
-    "resolve     ",
-    "resolve_skip",
-    "get_scoped_var",
-    "put_scoped_var",
-    "resolve_base",
+    "instanceof       ",
+    "typeof           ",
+    "in               ",
+
+    "resolve          ",
+    "resolve_skip     ",
+    "get_scoped_var   ",
+    "put_scoped_var   ",
+    "resolve_base     ",
     "resolve_with_base",
-    "resolve_func",
-    "get_by_id   ",
-    "put_by_id   ",
-    "del_by_id   ",
-    "get_by_val  ",
-    "put_by_val  ",
-    "del_by_val  ",
-    "put_by_index",
-    "put_getter  ",
-    "put_setter  ",
-
-    "jmp         ",
-    "jtrue       ",
-    "jfalse      ",
-    "jless       ",
-    "jnless      ",
-    "jmp_scopes  ",
-    "loop        ",
-    "loop_if_true",
-    "loop_if_less",
-
-    "new_func    ",
-    "new_func_exp",
-    "call        ",
-    "call_eval   ",
-    "ret         ",
-
-    "construct   ",
-
-    "get_pnames  ",
-    "next_pname  ",
-
-    "push_scope  ",
-    "pop_scope   ",
-
-    "catch       ",
-    "throw       ",
-    "new_error   ",
-
-    "jsr         ",
-    "sret        ",
-
-    "debug       ",
-
-    "end         "
+    "resolve_func     ",
+    "get_by_id        ",
+    "put_by_id        ",
+    "del_by_id        ",
+    "get_by_val       ",
+    "put_by_val       ",
+    "del_by_val       ",
+    "put_by_index     ",
+    "put_getter       ",
+    "put_setter       ",
+
+    "jmp              ",
+    "jtrue            ",
+    "jfalse           ",
+    "jless            ",
+    "jnless           ",
+    "jmp_scopes       ",
+    "loop             ",
+    "loop_if_true     ",
+    "loop_if_less     ",
+
+    "new_func         ",
+    "new_func_exp     ",
+    "call             ",
+    "call_eval        ",
+    "ret              ",
+
+    "construct        ",
+
+    "get_pnames       ",
+    "next_pname       ",
+
+    "push_scope       ",
+    "pop_scope        ",
+
+    "catch            ",
+    "throw            ",
+    "new_error        ",
+
+    "jsr              ",
+    "sret             ",
+
+    "debug            ",
+
+    "end              "
 };
 
+#endif
+
+
+void ScopeSampleRecord::sample(CodeBlock* codeBlock, Instruction* vPC)
+{
+    m_totalCount++;
+
+    if (!m_vpcCounts) {
+        m_size = codeBlock->instructions.size();
+        m_vpcCounts = static_cast<int*>(calloc(m_size, sizeof(int)));
+        m_codeBlock = codeBlock;
+    }
+
+    unsigned codeOffset = static_cast<unsigned>(reinterpret_cast<ptrdiff_t>(vPC) - reinterpret_cast<ptrdiff_t>(codeBlock->instructions.begin())) / sizeof(Instruction*);
+    // This could occur if codeBlock & vPC are not consistent - e.g. sample mid op_call/op_ret.
+    if (codeOffset < m_size)
+        m_vpcCounts[codeOffset]++;
+}
+
+static inline unsigned hertz2us(unsigned hertz)
+{
+    return 1000000 / hertz;
+}
+
+void SamplingTool::run()
+{
+    while (m_running) {
+        usleep(hertz2us(m_hertz));
+
+        m_totalSamples++;
+
+        CodeBlock* codeBlock = m_recordedCodeBlock;
+        Instruction* vPC = m_recordedVPC;
+
+        if (codeBlock && vPC) {
+            ScopeSampleRecord* record = m_scopeSampleMap->get(codeBlock->ownerNode);
+            if (record)
+                record->sample(codeBlock, vPC);
+        }
+    }
+}
+
+void* SamplingTool::threadStartFunc(void* samplingTool)
+{
+    reinterpret_cast<SamplingTool*>(samplingTool)->run();
+    return 0;
+}
+
+void SamplingTool::notifyOfScope(ScopeNode* scope)
+{
+    m_scopeSampleMap->set(scope, new ScopeSampleRecord(scope));
+}
+
+void SamplingTool::start(unsigned hertz)
+{
+    ASSERT(!m_running);
+    m_running = true;
+    m_hertz = hertz;
+    pthread_create(&m_samplingThread, 0, threadStartFunc, this);
+}
+
+void SamplingTool::stop()
+{
+    ASSERT(m_running);
+    m_running = false;
+    pthread_join(m_samplingThread, 0);
+}
+
+#if SAMPLING_TOOL_ENABLED
+
+struct OpcodeSampleInfo
+{
+    OpcodeID opcode;
+    long long count;
+};
+
+struct LineCountInfo
+{
+    unsigned line;
+    unsigned count;
+};
+
+static int compareLineCountInfoSampling(const void* left, const void* right)
+{
+    const LineCountInfo *leftLineCount = reinterpret_cast<const LineCountInfo *>(left);
+    const LineCountInfo *rightLineCount = reinterpret_cast<const LineCountInfo *>(right);
+
+    return (leftLineCount->line > rightLineCount->line) ? 1 : (leftLineCount->line < rightLineCount->line) ? -1 : 0;
+}
+
+static int compareOpcodeIndicesSampling(const void* left, const void* right)
+{
+    const OpcodeSampleInfo *leftSampleInfo = reinterpret_cast<const OpcodeSampleInfo *>(left);
+    const OpcodeSampleInfo *rightSampleInfo = reinterpret_cast<const OpcodeSampleInfo *>(right);
+
+    return (leftSampleInfo->count < rightSampleInfo->count) ? 1 : (leftSampleInfo->count > rightSampleInfo->count) ? -1 : 0;
+}
+
+static int compareScopeSampleRecords(const void* left, const void* right)
+{
+    const ScopeSampleRecord* const leftValue = *static_cast<const ScopeSampleRecord* const *>(left);
+    const ScopeSampleRecord* const rightValue = *static_cast<const ScopeSampleRecord* const *>(right);
+
+    return (leftValue->m_totalCount < rightValue->m_totalCount) ? 1 : (leftValue->m_totalCount > rightValue->m_totalCount) ? -1 : 0;
+}
+
+void SamplingTool::dump(ExecState* exec)
+{
+    // Tidies up SunSpider output by removing short scripts - such a small number of samples would likely not be useful anyhow.
+    if (m_totalSamples < 10)
+        return;
+    
+    // (1) Calculate 'totalCodeBlockSamples', build and sort 'codeBlockSamples' array.
+
+    int scopeCount = m_scopeSampleMap->size();
+    long long totalCodeBlockSamples = 0;
+    ScopeSampleRecord* codeBlockSamples[scopeCount];
+    ScopeSampleRecordMap::iterator iter = m_scopeSampleMap->begin();
+    for (int i=0; i < scopeCount; ++i, ++iter) {
+        codeBlockSamples[i] = iter->second;
+        totalCodeBlockSamples += codeBlockSamples[i]->m_totalCount;
+    }
+    mergesort(codeBlockSamples, scopeCount, sizeof(ScopeSampleRecord*), compareScopeSampleRecords);
+
+    // (2) Print data from 'codeBlockSamples' array, calculate 'totalOpcodeSamples', populate 'opcodeSampleCounts' array.
+
+    long long totalOpcodeSamples = 0;
+    long long opcodeSampleCounts[numOpcodeIDs] = { 0 };
+
+    fprintf(stdout, "\nBlock sampling results\n\n"); 
+    fprintf(stdout, "Total blocks sampled (total samples): %lld (%lld)\n\n", totalCodeBlockSamples, m_totalSamples);
+
+    for (int i=0; i < scopeCount; i++) {
+        ScopeSampleRecord *record = codeBlockSamples[i];
+        CodeBlock* codeBlock = record->m_codeBlock;
+
+        double totalPercent = (record->m_totalCount * 100.0)/m_totalSamples;
+        double blockPercent = (record->m_totalCount * 100.0)/totalCodeBlockSamples;
+
+        if ((blockPercent >= 1) && codeBlock) {
+            Instruction* code = codeBlock->instructions.begin();
+            fprintf(stdout, "#%d: %s:%d: sampled %d times - %.3f%% (%.3f%%)\n", i+1, record->m_scope->sourceURL().UTF8String().c_str(), codeBlock->lineNumberForVPC(code), record->m_totalCount, blockPercent, totalPercent);
+            if (i < 10) {
+                HashMap<unsigned,unsigned> lineCounts;
+                codeBlock->dump(exec);
+                for (unsigned op = 0; op < record->m_size; ++op) {
+                    int count = record->m_vpcCounts[op];
+                    if (count) {
+                        printf("    [% 4d] has sample count: % 4d\n", op, count);
+                        unsigned line = codeBlock->lineNumberForVPC(code+op);
+                        lineCounts.set(line, (lineCounts.contains(line) ? lineCounts.get(line) : 0) + count);
+                    }
+                }
+                printf("\n");
+                int linesCount = lineCounts.size();
+                LineCountInfo lineCountInfo[linesCount];
+                int lineno = 0;
+                for (HashMap<unsigned,unsigned>::iterator iter = lineCounts.begin(); iter != lineCounts.end(); ++iter, ++lineno) {
+                    lineCountInfo[lineno].line = iter->first;
+                    lineCountInfo[lineno].count = iter->second;
+                }
+                mergesort(lineCountInfo, linesCount, sizeof(LineCountInfo), compareLineCountInfoSampling);
+                for (lineno = 0; lineno < linesCount; ++lineno) {
+                    printf("    Line #%d has sample count %d.\n", lineCountInfo[lineno].line, lineCountInfo[lineno].count);
+                }
+                printf("\n");
+            }
+        }
+        
+        if (record->m_vpcCounts && codeBlock) {
+            Instruction* instructions = codeBlock->instructions.begin();
+            for (unsigned op = 0; op < record->m_size; ++op) {
+                Opcode opcode = instructions[op].u.opcode;
+                if (exec->machine()->isOpcode(opcode)) {
+                    totalOpcodeSamples += record->m_vpcCounts[op];
+                    opcodeSampleCounts[exec->machine()->getOpcodeID(opcode)] += record->m_vpcCounts[op];
+                }
+            }
+        }
+    }
+    printf("\n");
+
+    // (3) Build and sort 'opcodeSampleInfo' array.
+
+    OpcodeSampleInfo opcodeSampleInfo[numOpcodeIDs];
+    for (int i = 0; i < numOpcodeIDs; ++i) {
+        opcodeSampleInfo[i].opcode = (OpcodeID)i;
+        opcodeSampleInfo[i].count = opcodeSampleCounts[i];
+    }
+    mergesort(opcodeSampleInfo, numOpcodeIDs, sizeof(OpcodeSampleInfo), compareOpcodeIndicesSampling);
+
+    // (4) Print Opcode sampling results.
+    
+    fprintf(stdout, "\nOpcode sampling results\n\n"); 
+    
+    fprintf(stdout, "Total opcodes sampled (total samples): %lld (%lld)\n\n", totalOpcodeSamples, m_totalSamples);
+    fprintf(stdout, "Opcodes in order:\n\n");
+    for (int i = 0; i < numOpcodeIDs; ++i) {
+        long long count = opcodeSampleCounts[i];
+        fprintf(stdout, "%s:\t%6lld\t%.3f%%\t(%.3f%%)\n", opcodeNames[i], count, ((double)count * 100)/totalOpcodeSamples, ((double)count * 100)/m_totalSamples);    
+    }
+    fprintf(stdout, "\n");
+    fprintf(stdout, "Opcodes by sample count:\n\n");
+    for (int i = 0; i < numOpcodeIDs; ++i) {
+        OpcodeID opcode = opcodeSampleInfo[i].opcode;
+        long long count = opcodeSampleInfo[i].count;
+        fprintf(stdout, "%s:\t%6lld\t%.3f%%\t(%.3f%%)\n", opcodeNames[opcode], count, ((double)count * 100)/totalOpcodeSamples, ((double)count * 100)/m_totalSamples);    
+    }
+    fprintf(stdout, "\n");
+}
+
+#else
+
+void SamplingTool::dump(ExecState*)
+{
+}
+
+#endif
+
+
+#if DUMP_OPCODE_STATS
+
+long long OpcodeStats::opcodeCounts[numOpcodeIDs];
+long long OpcodeStats::opcodePairCounts[numOpcodeIDs][numOpcodeIDs];
+int OpcodeStats::lastOpcode = -1;
+
+static OpcodeStats logger;
+
 OpcodeStats::OpcodeStats()
 {
     for (int i = 0; i < numOpcodeIDs; ++i)
index d7850e5..60ac844 100644 (file)
 #define Opcodes_h
 
 #include <wtf/Assertions.h>
+#include <wtf/HashMap.h>
 
 namespace KJS {
 
+#define SAMPLING_TOOL_ENABLED 0
 #define DUMP_OPCODE_STATS 0
 
     #define FOR_EACH_OPCODE_ID(macro) \
@@ -143,6 +145,114 @@ namespace KJS {
     typedef OpcodeID Opcode;
 #endif
 
+    class ExecState;
+    class ScopeNode;
+    class CodeBlock;
+    struct Instruction;
+
+    struct ScopeSampleRecord
+    {
+        RefPtr<ScopeNode> m_scope;
+        CodeBlock* m_codeBlock;
+        int m_totalCount;
+        int* m_vpcCounts;
+        unsigned m_size;
+        
+        ScopeSampleRecord(ScopeNode* scope)
+            : m_scope(scope)
+            , m_codeBlock(0)
+            , m_totalCount(0)
+            , m_vpcCounts(0)
+            , m_size(0)
+        {
+        }
+        
+        ~ScopeSampleRecord()
+        {
+            if (m_vpcCounts)
+                free(m_vpcCounts);
+        }
+        
+        void sample(CodeBlock* codeBlock, Instruction* vPC);
+    };
+
+    typedef WTF::HashMap<ScopeNode*, ScopeSampleRecord*> ScopeSampleRecordMap;
+
+    class SamplingTool
+    {
+    public:
+        SamplingTool()
+            : m_running(false)
+            , m_recordedCodeBlock(0)
+            , m_recordedVPC(0)
+            , m_totalSamples(0)
+            , m_scopeSampleMap(new ScopeSampleRecordMap())
+        {
+        }
+
+        ~SamplingTool()
+        {
+            for (ScopeSampleRecordMap::iterator iter = m_scopeSampleMap->begin(); iter != m_scopeSampleMap->end(); ++iter)
+                delete iter->second;
+            delete m_scopeSampleMap;
+        }
+
+        void start(unsigned hertz=1000);
+        void stop();
+        void dump(ExecState*);
+
+        void notifyOfScope(ScopeNode* scope);
+
+        void sample(CodeBlock* recordedCodeBlock, Instruction* recordedVPC)
+        {
+            m_recordedCodeBlock = recordedCodeBlock;
+            m_recordedVPC = recordedVPC;
+        }
+
+        void privateExecuteReturned()
+        {
+            m_recordedCodeBlock = 0;
+            m_recordedVPC = 0;
+        }
+        
+        void callingNativeFunction()
+        {
+            m_recordedCodeBlock = 0;
+            m_recordedVPC = 0;
+        }
+        
+    private:
+        static void* threadStartFunc(void*);
+        void run();
+        
+        // Sampling thread state.
+        bool m_running;
+        unsigned m_hertz;
+        pthread_t m_samplingThread;
+
+        // State tracked by the main thread, used by the sampling thread.
+        CodeBlock* m_recordedCodeBlock;
+        Instruction* m_recordedVPC;
+
+        // Gathered sample data.
+        long long m_totalSamples;
+        ScopeSampleRecordMap* m_scopeSampleMap;
+    };
+
+// SCOPENODE_ / MACHINE_ macros for use from within member methods on ScopeNode / Machine respectively.
+#if SAMPLING_TOOL_ENABLED
+#define SCOPENODE_SAMPLING_notifyOfScope(sampler) sampler->notifyOfScope(this)
+#define MACHINE_SAMPLING_sample(codeBlock, vPC) m_sampler->sample(codeBlock, vPC)
+#define MACHINE_SAMPLING_privateExecuteReturned() m_sampler->privateExecuteReturned()
+#define MACHINE_SAMPLING_callingNativeFunction() m_sampler->callingNativeFunction()
+#else
+#define SCOPENODE_SAMPLING_notifyOfScope(sampler)
+#define MACHINE_SAMPLING_sample(codeBlock, vPC)
+#define MACHINE_SAMPLING_privateExecuteReturned()
+#define MACHINE_SAMPLING_callingNativeFunction()
+#endif
+
+
 #if DUMP_OPCODE_STATS
 
     struct OpcodeStats {
index 8f57112..2168caf 100644 (file)
@@ -339,6 +339,12 @@ static bool runWithScripts(GlobalObject* globalObject, const Vector<UString>& fi
         if (prettyPrint)
             prettyPrintScript(globalObject->globalExec(), fileName, script);
         else {
+#if SAMPLING_TOOL_ENABLED
+            Machine* machine = globalObject->globalData()->machine;
+            machine->m_sampler = new SamplingTool();
+            machine->m_sampler->start();
+#endif
+
             Completion completion = Interpreter::evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), fileName, 1, script.data());
             success = success && completion.complType() != Throw;
             if (dump) {
@@ -347,6 +353,12 @@ static bool runWithScripts(GlobalObject* globalObject, const Vector<UString>& fi
                 else
                     printf("Exception: %s\n", completion.value()->toString(globalObject->globalExec()).ascii());
             }
+
+#if SAMPLING_TOOL_ENABLED
+            machine->m_sampler->stop();
+            machine->m_sampler->dump(globalObject->globalExec());
+            delete machine->m_sampler;
+#endif
         }
     }
     return success;
index e325b07..54ff419 100644 (file)
@@ -1548,7 +1548,7 @@ RegisterID* TryNode::emitCode(CodeGenerator& generator, RegisterID* dst)
 }
 
 
-// ------------------------------ FunctionBodyNode -----------------------------
+// ------------------------------ ScopeNode -----------------------------
 
 ScopeNode::ScopeNode(JSGlobalData* globalData, SourceElements* children, VarStack* varStack, FunctionStack* funcStack, bool usesEval, bool needsClosure)
     : BlockNode(globalData, children)
@@ -1561,6 +1561,8 @@ ScopeNode::ScopeNode(JSGlobalData* globalData, SourceElements* children, VarStac
         m_varStack = *varStack;
     if (funcStack)
         m_functionStack = *funcStack;
+
+    SCOPENODE_SAMPLING_notifyOfScope(globalData->machine->m_sampler);
 }
 
 // ------------------------------ ProgramNode -----------------------------