⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 181887 in webkit


Ignore:
Timestamp:
Mar 23, 2015, 10:37:19 PM (11 years ago)
Author:
fpizlo@apple.com
Message:

JSC should have a low-cost asynchronous disassembler
https://bugs.webkit.org/show_bug.cgi?id=142997

Reviewed by Mark Lam.
Source/JavaScriptCore:


This adds a JSC_asyncDisassembly option that disassembles on a thread. Disassembly
doesn't block execution. Some code will live a little longer because of this, since the
work tasks hold a ref to the code, but other than that there is basically no overhead.

At present, this isn't really a replacement for JSC_showDisassembly, since it doesn't
provide contextual IR information for Baseline and DFG disassemblies, and it doesn't do
the separate IR dumps for FTL. Using JSC_showDisassembly and friends along with
JSC_asyncDisassembly has bizarre behavior - so just choose one.

A simple way of understanding how great this is, is to run a small benchmark like
V8Spider/earley-boyer.

Performance without any disassembly flags: 60ms
Performance with JSC_showDisassembly=true: 477ms
Performance with JSC_asyncDisassembly=true: 65ms

So, the overhead of disassembly goes from 8x to 8%.

Note that JSC_asyncDisassembly=true does make it incorrect to run "time" as a way of
measuring benchmark performance. This is because at VM exit, we wait for all async
disassembly requests to finish. For example, for earley-boyer, we spend an extra ~130ms
after the benchmark completely finishes to finish the disassemblies. This small weirdness
should be OK for the intended use-cases, since all you have to do to get around it is to
measure the execution time of the benchmark payload rather than the end-to-end time of
launching the VM.

  • assembler/LinkBuffer.cpp:

(JSC::LinkBuffer::finalizeCodeWithDisassembly):

  • assembler/LinkBuffer.h:

(JSC::LinkBuffer::wasAlreadyDisassembled):
(JSC::LinkBuffer::didAlreadyDisassemble):

  • dfg/DFGJITCompiler.cpp:

(JSC::DFG::JITCompiler::disassemble):

  • dfg/DFGJITFinalizer.cpp:

(JSC::DFG::JITFinalizer::finalize):
(JSC::DFG::JITFinalizer::finalizeFunction):

  • disassembler/Disassembler.cpp:

(JSC::disassembleAsynchronously):
(JSC::waitForAsynchronousDisassembly):

  • disassembler/Disassembler.h:
  • ftl/FTLCompile.cpp:

(JSC::FTL::mmAllocateDataSection):

  • ftl/FTLLink.cpp:

(JSC::FTL::link):

  • jit/JIT.cpp:

(JSC::JIT::privateCompile):

  • jsc.cpp:
  • runtime/Options.h:
  • runtime/VM.cpp:

(JSC::VM::~VM):

Source/WTF:

  • wtf/StringPrintStream.h:
Location:
trunk/Source
Files:
15 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r181871 r181887  
     12015-03-23  Filip Pizlo  <fpizlo@apple.com>
     2
     3        JSC should have a low-cost asynchronous disassembler
     4        https://bugs.webkit.org/show_bug.cgi?id=142997
     5
     6        Reviewed by Mark Lam.
     7       
     8        This adds a JSC_asyncDisassembly option that disassembles on a thread. Disassembly
     9        doesn't block execution. Some code will live a little longer because of this, since the
     10        work tasks hold a ref to the code, but other than that there is basically no overhead.
     11       
     12        At present, this isn't really a replacement for JSC_showDisassembly, since it doesn't
     13        provide contextual IR information for Baseline and DFG disassemblies, and it doesn't do
     14        the separate IR dumps for FTL. Using JSC_showDisassembly and friends along with
     15        JSC_asyncDisassembly has bizarre behavior - so just choose one.
     16       
     17        A simple way of understanding how great this is, is to run a small benchmark like
     18        V8Spider/earley-boyer.
     19       
     20        Performance without any disassembly flags: 60ms
     21        Performance with JSC_showDisassembly=true: 477ms
     22        Performance with JSC_asyncDisassembly=true: 65ms
     23       
     24        So, the overhead of disassembly goes from 8x to 8%.
     25       
     26        Note that JSC_asyncDisassembly=true does make it incorrect to run "time" as a way of
     27        measuring benchmark performance. This is because at VM exit, we wait for all async
     28        disassembly requests to finish. For example, for earley-boyer, we spend an extra ~130ms
     29        after the benchmark completely finishes to finish the disassemblies. This small weirdness
     30        should be OK for the intended use-cases, since all you have to do to get around it is to
     31        measure the execution time of the benchmark payload rather than the end-to-end time of
     32        launching the VM.
     33
     34        * assembler/LinkBuffer.cpp:
     35        (JSC::LinkBuffer::finalizeCodeWithDisassembly):
     36        * assembler/LinkBuffer.h:
     37        (JSC::LinkBuffer::wasAlreadyDisassembled):
     38        (JSC::LinkBuffer::didAlreadyDisassemble):
     39        * dfg/DFGJITCompiler.cpp:
     40        (JSC::DFG::JITCompiler::disassemble):
     41        * dfg/DFGJITFinalizer.cpp:
     42        (JSC::DFG::JITFinalizer::finalize):
     43        (JSC::DFG::JITFinalizer::finalizeFunction):
     44        * disassembler/Disassembler.cpp:
     45        (JSC::disassembleAsynchronously):
     46        (JSC::waitForAsynchronousDisassembly):
     47        * disassembler/Disassembler.h:
     48        * ftl/FTLCompile.cpp:
     49        (JSC::FTL::mmAllocateDataSection):
     50        * ftl/FTLLink.cpp:
     51        (JSC::FTL::link):
     52        * jit/JIT.cpp:
     53        (JSC::JIT::privateCompile):
     54        * jsc.cpp:
     55        * runtime/Options.h:
     56        * runtime/VM.cpp:
     57        (JSC::VM::~VM):
     58
    1592015-03-23  Dean Jackson  <dino@apple.com>
    260
  • trunk/Source/JavaScriptCore/assembler/LinkBuffer.cpp

    r171123 r181887  
    11/*
    2  * Copyright (C) 2012, 2013, 2014 Apple Inc. All rights reserved.
     2 * Copyright (C) 2012-2015 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    6060    CodeRef result = finalizeCodeWithoutDisassembly();
    6161
    62 #if ENABLE(DISASSEMBLER)
    63     dataLogF("Generated JIT code for ");
     62    if (m_alreadyDisassembled)
     63        return result;
     64   
     65    StringPrintStream out;
     66    out.printf("Generated JIT code for ");
    6467    va_list argList;
    6568    va_start(argList, format);
    66     WTF::dataLogFV(format, argList);
     69    out.vprintf(format, argList);
    6770    va_end(argList);
    68     dataLogF(":\n");
    69    
    70     dataLogF("    Code at [%p, %p):\n", result.code().executableAddress(), static_cast<char*>(result.code().executableAddress()) + result.size());
     71    out.printf(":\n");
     72
     73    out.printf("    Code at [%p, %p):\n", result.code().executableAddress(), static_cast<char*>(result.code().executableAddress()) + result.size());
     74   
     75    CString header = out.toCString();
     76   
     77    if (Options::asyncDisassembly()) {
     78        disassembleAsynchronously(header, result, m_size, "    ");
     79        return result;
     80    }
     81   
     82    dataLog(header);
    7183    disassemble(result.code(), m_size, "    ", WTF::dataFile());
    72 #else
    73     UNUSED_PARAM(format);
    74 #endif // ENABLE(DISASSEMBLER)
    7584   
    7685    return result;
  • trunk/Source/JavaScriptCore/assembler/LinkBuffer.h

    r171123 r181887  
    11/*
    2  * Copyright (C) 2009, 2010, 2012, 2013, 2014 Apple Inc. All rights reserved.
     2 * Copyright (C) 2009, 2010, 2012-2015 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    255255        return m_size;
    256256    }
     257   
     258    bool wasAlreadyDisassembled() const { return m_alreadyDisassembled; }
     259    void didAlreadyDisassemble() { m_alreadyDisassembled = true; }
    257260
    258261private:
     
    311314    bool m_completed;
    312315#endif
     316    bool m_alreadyDisassembled { false };
    313317};
    314318
     
    321325
    322326#define FINALIZE_CODE_FOR(codeBlock, linkBufferReference, dataLogFArgumentsForHeading)  \
    323     FINALIZE_CODE_IF(shouldShowDisassemblyFor(codeBlock), linkBufferReference, dataLogFArgumentsForHeading)
     327    FINALIZE_CODE_IF(shouldShowDisassemblyFor(codeBlock) || Options::asyncDisassembly(), linkBufferReference, dataLogFArgumentsForHeading)
    324328
    325329// Use this to finalize code, like so:
     
    340344
    341345#define FINALIZE_CODE(linkBufferReference, dataLogFArgumentsForHeading)  \
    342     FINALIZE_CODE_IF(JSC::Options::showDisassembly(), linkBufferReference, dataLogFArgumentsForHeading)
     346    FINALIZE_CODE_IF(JSC::Options::asyncDisassembly() || JSC::Options::showDisassembly(), linkBufferReference, dataLogFArgumentsForHeading)
    343347
    344348#define FINALIZE_DFG_CODE(linkBufferReference, dataLogFArgumentsForHeading)  \
    345     FINALIZE_CODE_IF((JSC::Options::showDisassembly() || Options::showDFGDisassembly()), linkBufferReference, dataLogFArgumentsForHeading)
     349    FINALIZE_CODE_IF(JSC::Options::asyncDisassembly() || JSC::Options::showDisassembly() || Options::showDFGDisassembly(), linkBufferReference, dataLogFArgumentsForHeading)
    346350
    347351} // namespace JSC
  • trunk/Source/JavaScriptCore/dfg/DFGJITCompiler.cpp

    r180423 r181887  
    455455void JITCompiler::disassemble(LinkBuffer& linkBuffer)
    456456{
    457     if (shouldShowDisassembly())
     457    if (shouldShowDisassembly()) {
    458458        m_disassembler->dump(linkBuffer);
     459        linkBuffer.didAlreadyDisassemble();
     460    }
    459461   
    460462    if (m_graph.m_plan.compilation)
  • trunk/Source/JavaScriptCore/dfg/DFGJITFinalizer.cpp

    r176533 r181887  
    11/*
    2  * Copyright (C) 2013 Apple Inc. All rights reserved.
     2 * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    3030
    3131#include "CodeBlock.h"
     32#include "CodeBlockWithJITType.h"
    3233#include "DFGCommon.h"
    3334#include "DFGPlan.h"
     
    5758{
    5859    m_jitCode->initializeCodeRef(
    59         m_linkBuffer->finalizeCodeWithoutDisassembly(), MacroAssemblerCodePtr());
     60        FINALIZE_DFG_CODE(*m_linkBuffer, ("DFG JIT code for %s", toCString(CodeBlockWithJITType(m_plan.codeBlock.get(), JITCode::DFGJIT)).data())),
     61        MacroAssemblerCodePtr());
     62   
    6063    m_plan.codeBlock->setJITCode(m_jitCode);
    6164   
     
    6972    RELEASE_ASSERT(!m_withArityCheck.isEmptyValue());
    7073    m_jitCode->initializeCodeRef(
    71         m_linkBuffer->finalizeCodeWithoutDisassembly(), m_withArityCheck);
     74        FINALIZE_DFG_CODE(*m_linkBuffer, ("DFG JIT code for %s", toCString(CodeBlockWithJITType(m_plan.codeBlock.get(), JITCode::DFGJIT)).data())),
     75        m_withArityCheck);
    7276    m_plan.codeBlock->setJITCode(m_jitCode);
    7377   
  • trunk/Source/JavaScriptCore/disassembler/Disassembler.cpp

    r163760 r181887  
    11/*
    2  * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
     2 * Copyright (C) 2012, 2013, 2015 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2929#include "MacroAssemblerCodeRef.h"
    3030#include <wtf/DataLog.h>
     31#include <wtf/Deque.h>
     32#include <wtf/NeverDestroyed.h>
     33#include <wtf/StringPrintStream.h>
     34#include <wtf/Threading.h>
     35#include <wtf/ThreadingPrimitives.h>
    3136
    3237namespace JSC {
     
    4045}
    4146
     47namespace {
     48
     49// This is really a struct, except that it should be a class because that's what the WTF_* macros
     50// expect.
     51class DisassemblyTask {
     52    WTF_MAKE_NONCOPYABLE(DisassemblyTask);
     53    WTF_MAKE_FAST_ALLOCATED;
     54public:
     55    DisassemblyTask()
     56    {
     57    }
     58   
     59    ~DisassemblyTask()
     60    {
     61        if (header)
     62            free(header); // free() because it would have been copied by strdup.
     63    }
     64   
     65    char* header { nullptr };
     66    MacroAssemblerCodeRef codeRef;
     67    size_t size { 0 };
     68    const char* prefix { nullptr };
     69    InstructionSubsetHint subsetHint { MacroAssemblerSubset };
     70};
     71
     72class AsynchronousDisassembler {
     73public:
     74    AsynchronousDisassembler()
     75    {
     76        createThread("Asynchronous Disassembler", [&] () { run(); });
     77    }
     78   
     79    void enqueue(std::unique_ptr<DisassemblyTask> task)
     80    {
     81        MutexLocker locker(m_lock);
     82        m_queue.append(WTF::move(task));
     83        m_condition.broadcast();
     84    }
     85   
     86    void waitUntilEmpty()
     87    {
     88        MutexLocker locker(m_lock);
     89        while (!m_queue.isEmpty() || m_working)
     90            m_condition.wait(m_lock);
     91    }
     92   
     93private:
     94    NO_RETURN void run()
     95    {
     96        for (;;) {
     97            std::unique_ptr<DisassemblyTask> task;
     98            {
     99                MutexLocker locker(m_lock);
     100                m_working = false;
     101                m_condition.broadcast();
     102                while (m_queue.isEmpty())
     103                    m_condition.wait(m_lock);
     104                task = m_queue.takeFirst();
     105                m_working = true;
     106            }
     107
     108            dataLog(task->header);
     109            disassemble(
     110                task->codeRef.code(), task->size, task->prefix, WTF::dataFile(),
     111                task->subsetHint);
     112        }
     113    }
     114   
     115    Mutex m_lock;
     116    ThreadCondition m_condition;
     117    Deque<std::unique_ptr<DisassemblyTask>> m_queue;
     118    bool m_working { false };
     119};
     120
     121bool hadAnyAsynchronousDisassembly = false;
     122
     123AsynchronousDisassembler& asynchronousDisassembler()
     124{
     125    static NeverDestroyed<AsynchronousDisassembler> disassembler;
     126    hadAnyAsynchronousDisassembly = true;
     127    return disassembler.get();
     128}
     129
     130} // anonymous namespace
     131
     132void disassembleAsynchronously(
     133    const CString& header, const MacroAssemblerCodeRef& codeRef, size_t size, const char* prefix,
     134    InstructionSubsetHint subsetHint)
     135{
     136    std::unique_ptr<DisassemblyTask> task = std::make_unique<DisassemblyTask>();
     137    task->header = strdup(header.data()); // Yuck! We need this because CString does racy refcounting.
     138    task->codeRef = codeRef;
     139    task->size = size;
     140    task->prefix = prefix;
     141    task->subsetHint = subsetHint;
     142   
     143    asynchronousDisassembler().enqueue(WTF::move(task));
     144}
     145
     146void waitForAsynchronousDisassembly()
     147{
     148    if (!hadAnyAsynchronousDisassembly)
     149        return;
     150   
     151    asynchronousDisassembler().waitUntilEmpty();
     152}
     153
    42154} // namespace JSC
    43155
  • trunk/Source/JavaScriptCore/disassembler/Disassembler.h

    r164424 r181887  
    11/*
    2  * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
     2 * Copyright (C) 2012, 2013, 2015 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2727#define Disassembler_h
    2828
     29#include <functional>
    2930#include <wtf/PrintStream.h>
     31#include <wtf/text/CString.h>
    3032
    3133namespace JSC {
    3234
    3335class MacroAssemblerCodePtr;
     36class MacroAssemblerCodeRef;
    3437
    3538enum InstructionSubsetHint { MacroAssemblerSubset, LLVMSubset };
     
    4851void disassemble(const MacroAssemblerCodePtr&, size_t, const char* prefix, PrintStream& out, InstructionSubsetHint = MacroAssemblerSubset);
    4952
     53// Asynchronous disassembly. This happens on another thread, and calls the provided
     54// callback when the disassembly is done.
     55void disassembleAsynchronously(
     56    const CString& header, const MacroAssemblerCodeRef&, size_t, const char* prefix,
     57    InstructionSubsetHint = MacroAssemblerSubset);
     58
     59JS_EXPORT_PRIVATE void waitForAsynchronousDisassembly();
     60
    5061} // namespace JSC
    5162
  • trunk/Source/JavaScriptCore/ftl/FTLCompile.cpp

    r180903 r181887  
    804804            recordMap, didSeeUnwindInfo);
    805805       
    806         if (shouldShowDisassembly()) {
     806        if (shouldShowDisassembly() || Options::asyncDisassembly()) {
    807807            for (unsigned i = 0; i < state.jitCode->handles().size(); ++i) {
    808808                if (state.codeSectionNames[i] != SECTION_NAME("text"))
     
    810810               
    811811                ExecutableMemoryHandle* handle = state.jitCode->handles()[i].get();
    812                 dataLog(
     812               
     813                CString header = toCString(
    813814                    "Generated LLVM code after stackmap-based fix-up for ",
    814815                    CodeBlockWithJITType(state.graph.m_codeBlock, JITCode::FTLJIT),
    815816                    " in ", state.graph.m_plan.mode, " #", i, ", ",
    816817                    state.codeSectionNames[i], ":\n");
     818               
     819                if (Options::asyncDisassembly()) {
     820                    disassembleAsynchronously(
     821                        header, MacroAssemblerCodeRef(handle), handle->sizeInBytes(), "    ",
     822                        LLVMSubset);
     823                    continue;
     824                }
     825               
     826                dataLog(header);
    817827                disassemble(
    818828                    MacroAssemblerCodePtr(handle->start()), handle->sizeInBytes(),
  • trunk/Source/JavaScriptCore/ftl/FTLLink.cpp

    r176572 r181887  
    132132                continue;
    133133           
    134                 ExecutableMemoryHandle* handle = state.jitCode->handles()[i].get();
    135                 disassemble(
    136                     MacroAssemblerCodePtr(handle->start()), handle->sizeInBytes(),
    137                     "      ", out, LLVMSubset);
     134            ExecutableMemoryHandle* handle = state.jitCode->handles()[i].get();
     135            disassemble(
     136                MacroAssemblerCodePtr(handle->start()), handle->sizeInBytes(),
     137                "      ", out, LLVMSubset);
    138138        }
    139139        compilation->addDescription(Profiler::OriginStack(), out.toCString());
  • trunk/Source/JavaScriptCore/jit/JIT.cpp

    r181466 r181887  
    11/*
    2  * Copyright (C) 2008, 2009, 2012, 2013, 2014 Apple Inc. All rights reserved.
     2 * Copyright (C) 2008, 2009, 2012-2015 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    3232#include "ArityCheckFailReturnThunks.h"
    3333#include "CodeBlock.h"
     34#include "CodeBlockWithJITType.h"
    3435#include "DFGCapabilities.h"
    3536#include "Interpreter.h"
     
    683684        withArityCheck = patchBuffer.locationOf(arityCheck);
    684685
    685     if (Options::showDisassembly())
     686    if (Options::showDisassembly()) {
    686687        m_disassembler->dump(patchBuffer);
     688        patchBuffer.didAlreadyDisassemble();
     689    }
    687690    if (m_compilation) {
    688691        m_disassembler->reportToProfiler(m_compilation.get(), patchBuffer);
     
    690693    }
    691694   
    692     CodeRef result = patchBuffer.finalizeCodeWithoutDisassembly();
     695    CodeRef result = FINALIZE_CODE(
     696        patchBuffer,
     697        ("Baseline JIT code for %s", toCString(CodeBlockWithJITType(m_codeBlock, JITCode::BaselineJIT)).data()));
    693698   
    694699    m_vm->machineCodeBytesPerBytecodeWordForBaselineJIT.add(
  • trunk/Source/JavaScriptCore/jsc.cpp

    r181326 r181887  
    2929#include "Completion.h"
    3030#include "CopiedSpaceInlines.h"
     31#include "Disassembler.h"
    3132#include "ExceptionHelpers.h"
    3233#include "HeapStatistics.h"
     
    105106NO_RETURN_WITH_VALUE static void jscExit(int status)
    106107{
     108    waitForAsynchronousDisassembly();
     109   
    107110#if ENABLE(DFG_JIT)
    108111    if (DFG::isCrashing()) {
  • trunk/Source/JavaScriptCore/runtime/Options.h

    r180423 r181887  
    117117    /* showDisassembly implies showDFGDisassembly. */ \
    118118    v(bool, showDisassembly, false) \
     119    v(bool, asyncDisassembly, false) \
    119120    v(bool, showDFGDisassembly, false) \
    120121    v(bool, showFTLDisassembly, false) \
  • trunk/Source/JavaScriptCore/runtime/VM.cpp

    r181458 r181887  
    4141#include "DFGLongLivedState.h"
    4242#include "DFGWorklist.h"
     43#include "Disassembler.h"
    4344#include "ErrorInstance.h"
    4445#include "FTLThunks.h"
     
    306307#endif // ENABLE(DFG_JIT)
    307308   
     309    waitForAsynchronousDisassembly();
     310   
    308311    // Clear this first to ensure that nobody tries to remove themselves from it.
    309312    m_perBytecodeProfiler = nullptr;
  • trunk/Source/WTF/ChangeLog

    r181845 r181887  
     12015-03-23  Filip Pizlo  <fpizlo@apple.com>
     2
     3        JSC should have a low-cost asynchronous disassembler
     4        https://bugs.webkit.org/show_bug.cgi?id=142997
     5
     6        Reviewed by Mark Lam.
     7
     8        * wtf/StringPrintStream.h:
     9
    1102015-03-22  Benjamin Poulain  <benjamin@webkit.org>
    211
  • trunk/Source/WTF/wtf/StringPrintStream.h

    r164384 r181887  
    3838    WTF_EXPORT_PRIVATE virtual ~StringPrintStream();
    3939   
    40     virtual void vprintf(const char* format, va_list) override WTF_ATTRIBUTE_PRINTF(2, 0);
     40    WTF_EXPORT_PRIVATE virtual void vprintf(const char* format, va_list) override WTF_ATTRIBUTE_PRINTF(2, 0);
    4141   
    4242    WTF_EXPORT_PRIVATE CString toCString();
Note: See TracChangeset for help on using the changeset viewer.