Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (181886 => 181887)
--- trunk/Source/_javascript_Core/ChangeLog 2015-03-24 01:48:54 UTC (rev 181886)
+++ trunk/Source/_javascript_Core/ChangeLog 2015-03-24 05:37:19 UTC (rev 181887)
@@ -1,3 +1,61 @@
+2015-03-23 Filip Pizlo <[email protected]>
+
+ JSC should have a low-cost asynchronous disassembler
+ https://bugs.webkit.org/show_bug.cgi?id=142997
+
+ Reviewed by Mark Lam.
+
+ 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):
+
2015-03-23 Dean Jackson <[email protected]>
ES7: Implement Array.prototype.includes
Modified: trunk/Source/_javascript_Core/assembler/LinkBuffer.cpp (181886 => 181887)
--- trunk/Source/_javascript_Core/assembler/LinkBuffer.cpp 2015-03-24 01:48:54 UTC (rev 181886)
+++ trunk/Source/_javascript_Core/assembler/LinkBuffer.cpp 2015-03-24 05:37:19 UTC (rev 181887)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012, 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -59,19 +59,28 @@
{
CodeRef result = finalizeCodeWithoutDisassembly();
-#if ENABLE(DISASSEMBLER)
- dataLogF("Generated JIT code for ");
+ if (m_alreadyDisassembled)
+ return result;
+
+ StringPrintStream out;
+ out.printf("Generated JIT code for ");
va_list argList;
va_start(argList, format);
- WTF::dataLogFV(format, argList);
+ out.vprintf(format, argList);
va_end(argList);
- dataLogF(":\n");
+ out.printf(":\n");
+
+ out.printf(" Code at [%p, %p):\n", result.code().executableAddress(), static_cast<char*>(result.code().executableAddress()) + result.size());
- dataLogF(" Code at [%p, %p):\n", result.code().executableAddress(), static_cast<char*>(result.code().executableAddress()) + result.size());
+ CString header = out.toCString();
+
+ if (Options::asyncDisassembly()) {
+ disassembleAsynchronously(header, result, m_size, " ");
+ return result;
+ }
+
+ dataLog(header);
disassemble(result.code(), m_size, " ", WTF::dataFile());
-#else
- UNUSED_PARAM(format);
-#endif // ENABLE(DISASSEMBLER)
return result;
}
Modified: trunk/Source/_javascript_Core/assembler/LinkBuffer.h (181886 => 181887)
--- trunk/Source/_javascript_Core/assembler/LinkBuffer.h 2015-03-24 01:48:54 UTC (rev 181886)
+++ trunk/Source/_javascript_Core/assembler/LinkBuffer.h 2015-03-24 05:37:19 UTC (rev 181887)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009, 2010, 2012, 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2009, 2010, 2012-2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -254,6 +254,9 @@
{
return m_size;
}
+
+ bool wasAlreadyDisassembled() const { return m_alreadyDisassembled; }
+ void didAlreadyDisassemble() { m_alreadyDisassembled = true; }
private:
#if ENABLE(BRANCH_COMPACTION)
@@ -310,6 +313,7 @@
#ifndef NDEBUG
bool m_completed;
#endif
+ bool m_alreadyDisassembled { false };
};
#define FINALIZE_CODE_IF(condition, linkBufferReference, dataLogFArgumentsForHeading) \
@@ -320,7 +324,7 @@
bool shouldShowDisassemblyFor(CodeBlock*);
#define FINALIZE_CODE_FOR(codeBlock, linkBufferReference, dataLogFArgumentsForHeading) \
- FINALIZE_CODE_IF(shouldShowDisassemblyFor(codeBlock), linkBufferReference, dataLogFArgumentsForHeading)
+ FINALIZE_CODE_IF(shouldShowDisassemblyFor(codeBlock) || Options::asyncDisassembly(), linkBufferReference, dataLogFArgumentsForHeading)
// Use this to finalize code, like so:
//
@@ -339,10 +343,10 @@
// is true, so you can hide expensive disassembly-only computations inside there.
#define FINALIZE_CODE(linkBufferReference, dataLogFArgumentsForHeading) \
- FINALIZE_CODE_IF(JSC::Options::showDisassembly(), linkBufferReference, dataLogFArgumentsForHeading)
+ FINALIZE_CODE_IF(JSC::Options::asyncDisassembly() || JSC::Options::showDisassembly(), linkBufferReference, dataLogFArgumentsForHeading)
#define FINALIZE_DFG_CODE(linkBufferReference, dataLogFArgumentsForHeading) \
- FINALIZE_CODE_IF((JSC::Options::showDisassembly() || Options::showDFGDisassembly()), linkBufferReference, dataLogFArgumentsForHeading)
+ FINALIZE_CODE_IF(JSC::Options::asyncDisassembly() || JSC::Options::showDisassembly() || Options::showDFGDisassembly(), linkBufferReference, dataLogFArgumentsForHeading)
} // namespace JSC
Modified: trunk/Source/_javascript_Core/dfg/DFGJITCompiler.cpp (181886 => 181887)
--- trunk/Source/_javascript_Core/dfg/DFGJITCompiler.cpp 2015-03-24 01:48:54 UTC (rev 181886)
+++ trunk/Source/_javascript_Core/dfg/DFGJITCompiler.cpp 2015-03-24 05:37:19 UTC (rev 181887)
@@ -454,8 +454,10 @@
void JITCompiler::disassemble(LinkBuffer& linkBuffer)
{
- if (shouldShowDisassembly())
+ if (shouldShowDisassembly()) {
m_disassembler->dump(linkBuffer);
+ linkBuffer.didAlreadyDisassemble();
+ }
if (m_graph.m_plan.compilation)
m_disassembler->reportToProfiler(m_graph.m_plan.compilation.get(), linkBuffer);
Modified: trunk/Source/_javascript_Core/dfg/DFGJITFinalizer.cpp (181886 => 181887)
--- trunk/Source/_javascript_Core/dfg/DFGJITFinalizer.cpp 2015-03-24 01:48:54 UTC (rev 181886)
+++ trunk/Source/_javascript_Core/dfg/DFGJITFinalizer.cpp 2015-03-24 05:37:19 UTC (rev 181887)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -29,6 +29,7 @@
#if ENABLE(DFG_JIT)
#include "CodeBlock.h"
+#include "CodeBlockWithJITType.h"
#include "DFGCommon.h"
#include "DFGPlan.h"
#include "JSCInlines.h"
@@ -56,7 +57,9 @@
bool JITFinalizer::finalize()
{
m_jitCode->initializeCodeRef(
- m_linkBuffer->finalizeCodeWithoutDisassembly(), MacroAssemblerCodePtr());
+ FINALIZE_DFG_CODE(*m_linkBuffer, ("DFG JIT code for %s", toCString(CodeBlockWithJITType(m_plan.codeBlock.get(), JITCode::DFGJIT)).data())),
+ MacroAssemblerCodePtr());
+
m_plan.codeBlock->setJITCode(m_jitCode);
finalizeCommon();
@@ -68,7 +71,8 @@
{
RELEASE_ASSERT(!m_withArityCheck.isEmptyValue());
m_jitCode->initializeCodeRef(
- m_linkBuffer->finalizeCodeWithoutDisassembly(), m_withArityCheck);
+ FINALIZE_DFG_CODE(*m_linkBuffer, ("DFG JIT code for %s", toCString(CodeBlockWithJITType(m_plan.codeBlock.get(), JITCode::DFGJIT)).data())),
+ m_withArityCheck);
m_plan.codeBlock->setJITCode(m_jitCode);
finalizeCommon();
Modified: trunk/Source/_javascript_Core/disassembler/Disassembler.cpp (181886 => 181887)
--- trunk/Source/_javascript_Core/disassembler/Disassembler.cpp 2015-03-24 01:48:54 UTC (rev 181886)
+++ trunk/Source/_javascript_Core/disassembler/Disassembler.cpp 2015-03-24 05:37:19 UTC (rev 181887)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 2013, 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -28,6 +28,11 @@
#include "MacroAssemblerCodeRef.h"
#include <wtf/DataLog.h>
+#include <wtf/Deque.h>
+#include <wtf/NeverDestroyed.h>
+#include <wtf/StringPrintStream.h>
+#include <wtf/Threading.h>
+#include <wtf/ThreadingPrimitives.h>
namespace JSC {
@@ -39,5 +44,112 @@
out.printf("%sdisassembly not available for range %p...%p\n", prefix, codePtr.executableAddress(), static_cast<char*>(codePtr.executableAddress()) + size);
}
+namespace {
+
+// This is really a struct, except that it should be a class because that's what the WTF_* macros
+// expect.
+class DisassemblyTask {
+ WTF_MAKE_NONCOPYABLE(DisassemblyTask);
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ DisassemblyTask()
+ {
+ }
+
+ ~DisassemblyTask()
+ {
+ if (header)
+ free(header); // free() because it would have been copied by strdup.
+ }
+
+ char* header { nullptr };
+ MacroAssemblerCodeRef codeRef;
+ size_t size { 0 };
+ const char* prefix { nullptr };
+ InstructionSubsetHint subsetHint { MacroAssemblerSubset };
+};
+
+class AsynchronousDisassembler {
+public:
+ AsynchronousDisassembler()
+ {
+ createThread("Asynchronous Disassembler", [&] () { run(); });
+ }
+
+ void enqueue(std::unique_ptr<DisassemblyTask> task)
+ {
+ MutexLocker locker(m_lock);
+ m_queue.append(WTF::move(task));
+ m_condition.broadcast();
+ }
+
+ void waitUntilEmpty()
+ {
+ MutexLocker locker(m_lock);
+ while (!m_queue.isEmpty() || m_working)
+ m_condition.wait(m_lock);
+ }
+
+private:
+ NO_RETURN void run()
+ {
+ for (;;) {
+ std::unique_ptr<DisassemblyTask> task;
+ {
+ MutexLocker locker(m_lock);
+ m_working = false;
+ m_condition.broadcast();
+ while (m_queue.isEmpty())
+ m_condition.wait(m_lock);
+ task = m_queue.takeFirst();
+ m_working = true;
+ }
+
+ dataLog(task->header);
+ disassemble(
+ task->codeRef.code(), task->size, task->prefix, WTF::dataFile(),
+ task->subsetHint);
+ }
+ }
+
+ Mutex m_lock;
+ ThreadCondition m_condition;
+ Deque<std::unique_ptr<DisassemblyTask>> m_queue;
+ bool m_working { false };
+};
+
+bool hadAnyAsynchronousDisassembly = false;
+
+AsynchronousDisassembler& asynchronousDisassembler()
+{
+ static NeverDestroyed<AsynchronousDisassembler> disassembler;
+ hadAnyAsynchronousDisassembly = true;
+ return disassembler.get();
+}
+
+} // anonymous namespace
+
+void disassembleAsynchronously(
+ const CString& header, const MacroAssemblerCodeRef& codeRef, size_t size, const char* prefix,
+ InstructionSubsetHint subsetHint)
+{
+ std::unique_ptr<DisassemblyTask> task = std::make_unique<DisassemblyTask>();
+ task->header = strdup(header.data()); // Yuck! We need this because CString does racy refcounting.
+ task->codeRef = codeRef;
+ task->size = size;
+ task->prefix = prefix;
+ task->subsetHint = subsetHint;
+
+ asynchronousDisassembler().enqueue(WTF::move(task));
+}
+
+void waitForAsynchronousDisassembly()
+{
+ if (!hadAnyAsynchronousDisassembly)
+ return;
+
+ asynchronousDisassembler().waitUntilEmpty();
+}
+
} // namespace JSC
Modified: trunk/Source/_javascript_Core/disassembler/Disassembler.h (181886 => 181887)
--- trunk/Source/_javascript_Core/disassembler/Disassembler.h 2015-03-24 01:48:54 UTC (rev 181886)
+++ trunk/Source/_javascript_Core/disassembler/Disassembler.h 2015-03-24 05:37:19 UTC (rev 181887)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 2013, 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -26,11 +26,14 @@
#ifndef Disassembler_h
#define Disassembler_h
+#include <functional>
#include <wtf/PrintStream.h>
+#include <wtf/text/CString.h>
namespace JSC {
class MacroAssemblerCodePtr;
+class MacroAssemblerCodeRef;
enum InstructionSubsetHint { MacroAssemblerSubset, LLVMSubset };
@@ -47,6 +50,14 @@
// the range of machine code addresses.
void disassemble(const MacroAssemblerCodePtr&, size_t, const char* prefix, PrintStream& out, InstructionSubsetHint = MacroAssemblerSubset);
+// Asynchronous disassembly. This happens on another thread, and calls the provided
+// callback when the disassembly is done.
+void disassembleAsynchronously(
+ const CString& header, const MacroAssemblerCodeRef&, size_t, const char* prefix,
+ InstructionSubsetHint = MacroAssemblerSubset);
+
+JS_EXPORT_PRIVATE void waitForAsynchronousDisassembly();
+
} // namespace JSC
#endif // Disassembler_h
Modified: trunk/Source/_javascript_Core/ftl/FTLCompile.cpp (181886 => 181887)
--- trunk/Source/_javascript_Core/ftl/FTLCompile.cpp 2015-03-24 01:48:54 UTC (rev 181886)
+++ trunk/Source/_javascript_Core/ftl/FTLCompile.cpp 2015-03-24 05:37:19 UTC (rev 181887)
@@ -803,17 +803,27 @@
state, state.graph.m_codeBlock, state.jitCode.get(), state.generatedFunction,
recordMap, didSeeUnwindInfo);
- if (shouldShowDisassembly()) {
+ if (shouldShowDisassembly() || Options::asyncDisassembly()) {
for (unsigned i = 0; i < state.jitCode->handles().size(); ++i) {
if (state.codeSectionNames[i] != SECTION_NAME("text"))
continue;
ExecutableMemoryHandle* handle = state.jitCode->handles()[i].get();
- dataLog(
+
+ CString header = toCString(
"Generated LLVM code after stackmap-based fix-up for ",
CodeBlockWithJITType(state.graph.m_codeBlock, JITCode::FTLJIT),
" in ", state.graph.m_plan.mode, " #", i, ", ",
state.codeSectionNames[i], ":\n");
+
+ if (Options::asyncDisassembly()) {
+ disassembleAsynchronously(
+ header, MacroAssemblerCodeRef(handle), handle->sizeInBytes(), " ",
+ LLVMSubset);
+ continue;
+ }
+
+ dataLog(header);
disassemble(
MacroAssemblerCodePtr(handle->start()), handle->sizeInBytes(),
" ", WTF::dataFile(), LLVMSubset);
Modified: trunk/Source/_javascript_Core/ftl/FTLLink.cpp (181886 => 181887)
--- trunk/Source/_javascript_Core/ftl/FTLLink.cpp 2015-03-24 01:48:54 UTC (rev 181886)
+++ trunk/Source/_javascript_Core/ftl/FTLLink.cpp 2015-03-24 05:37:19 UTC (rev 181887)
@@ -131,10 +131,10 @@
if (state.codeSectionNames[i] != SECTION_NAME("text"))
continue;
- ExecutableMemoryHandle* handle = state.jitCode->handles()[i].get();
- disassemble(
- MacroAssemblerCodePtr(handle->start()), handle->sizeInBytes(),
- " ", out, LLVMSubset);
+ ExecutableMemoryHandle* handle = state.jitCode->handles()[i].get();
+ disassemble(
+ MacroAssemblerCodePtr(handle->start()), handle->sizeInBytes(),
+ " ", out, LLVMSubset);
}
compilation->addDescription(Profiler::OriginStack(), out.toCString());
out.reset();
Modified: trunk/Source/_javascript_Core/jit/JIT.cpp (181886 => 181887)
--- trunk/Source/_javascript_Core/jit/JIT.cpp 2015-03-24 01:48:54 UTC (rev 181886)
+++ trunk/Source/_javascript_Core/jit/JIT.cpp 2015-03-24 05:37:19 UTC (rev 181887)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2009, 2012, 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2009, 2012-2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -31,6 +31,7 @@
#include "ArityCheckFailReturnThunks.h"
#include "CodeBlock.h"
+#include "CodeBlockWithJITType.h"
#include "DFGCapabilities.h"
#include "Interpreter.h"
#include "JITInlines.h"
@@ -682,14 +683,18 @@
if (m_codeBlock->codeType() == FunctionCode)
withArityCheck = patchBuffer.locationOf(arityCheck);
- if (Options::showDisassembly())
+ if (Options::showDisassembly()) {
m_disassembler->dump(patchBuffer);
+ patchBuffer.didAlreadyDisassemble();
+ }
if (m_compilation) {
m_disassembler->reportToProfiler(m_compilation.get(), patchBuffer);
m_vm->m_perBytecodeProfiler->addCompilation(m_compilation);
}
- CodeRef result = patchBuffer.finalizeCodeWithoutDisassembly();
+ CodeRef result = FINALIZE_CODE(
+ patchBuffer,
+ ("Baseline JIT code for %s", toCString(CodeBlockWithJITType(m_codeBlock, JITCode::BaselineJIT)).data()));
m_vm->machineCodeBytesPerBytecodeWordForBaselineJIT.add(
static_cast<double>(result.size()) /
Modified: trunk/Source/_javascript_Core/jsc.cpp (181886 => 181887)
--- trunk/Source/_javascript_Core/jsc.cpp 2015-03-24 01:48:54 UTC (rev 181886)
+++ trunk/Source/_javascript_Core/jsc.cpp 2015-03-24 05:37:19 UTC (rev 181887)
@@ -28,6 +28,7 @@
#include "CodeBlock.h"
#include "Completion.h"
#include "CopiedSpaceInlines.h"
+#include "Disassembler.h"
#include "ExceptionHelpers.h"
#include "HeapStatistics.h"
#include "InitializeThreading.h"
@@ -104,6 +105,8 @@
NO_RETURN_WITH_VALUE static void jscExit(int status)
{
+ waitForAsynchronousDisassembly();
+
#if ENABLE(DFG_JIT)
if (DFG::isCrashing()) {
for (;;) {
Modified: trunk/Source/_javascript_Core/runtime/Options.h (181886 => 181887)
--- trunk/Source/_javascript_Core/runtime/Options.h 2015-03-24 01:48:54 UTC (rev 181886)
+++ trunk/Source/_javascript_Core/runtime/Options.h 2015-03-24 05:37:19 UTC (rev 181887)
@@ -116,6 +116,7 @@
\
/* showDisassembly implies showDFGDisassembly. */ \
v(bool, showDisassembly, false) \
+ v(bool, asyncDisassembly, false) \
v(bool, showDFGDisassembly, false) \
v(bool, showFTLDisassembly, false) \
v(bool, showAllDFGNodes, false) \
Modified: trunk/Source/_javascript_Core/runtime/VM.cpp (181886 => 181887)
--- trunk/Source/_javascript_Core/runtime/VM.cpp 2015-03-24 01:48:54 UTC (rev 181886)
+++ trunk/Source/_javascript_Core/runtime/VM.cpp 2015-03-24 05:37:19 UTC (rev 181887)
@@ -40,6 +40,7 @@
#include "CustomGetterSetter.h"
#include "DFGLongLivedState.h"
#include "DFGWorklist.h"
+#include "Disassembler.h"
#include "ErrorInstance.h"
#include "FTLThunks.h"
#include "FunctionConstructor.h"
@@ -305,6 +306,8 @@
}
#endif // ENABLE(DFG_JIT)
+ waitForAsynchronousDisassembly();
+
// Clear this first to ensure that nobody tries to remove themselves from it.
m_perBytecodeProfiler = nullptr;
Modified: trunk/Source/WTF/ChangeLog (181886 => 181887)
--- trunk/Source/WTF/ChangeLog 2015-03-24 01:48:54 UTC (rev 181886)
+++ trunk/Source/WTF/ChangeLog 2015-03-24 05:37:19 UTC (rev 181887)
@@ -1,3 +1,12 @@
+2015-03-23 Filip Pizlo <[email protected]>
+
+ JSC should have a low-cost asynchronous disassembler
+ https://bugs.webkit.org/show_bug.cgi?id=142997
+
+ Reviewed by Mark Lam.
+
+ * wtf/StringPrintStream.h:
+
2015-03-22 Benjamin Poulain <[email protected]>
CSS Selectors: fix attribute case-insensitive matching of Contain and List
Modified: trunk/Source/WTF/wtf/StringPrintStream.h (181886 => 181887)
--- trunk/Source/WTF/wtf/StringPrintStream.h 2015-03-24 01:48:54 UTC (rev 181886)
+++ trunk/Source/WTF/wtf/StringPrintStream.h 2015-03-24 05:37:19 UTC (rev 181887)
@@ -37,7 +37,7 @@
WTF_EXPORT_PRIVATE StringPrintStream();
WTF_EXPORT_PRIVATE virtual ~StringPrintStream();
- virtual void vprintf(const char* format, va_list) override WTF_ATTRIBUTE_PRINTF(2, 0);
+ WTF_EXPORT_PRIVATE virtual void vprintf(const char* format, va_list) override WTF_ATTRIBUTE_PRINTF(2, 0);
WTF_EXPORT_PRIVATE CString toCString();
WTF_EXPORT_PRIVATE String toString();