|  | /* | 
|  | * Copyright (C) 2012-2018 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. | 
|  | * | 
|  | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR | 
|  | * 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 "Disassembler.h" | 
|  |  | 
|  | #include "MacroAssemblerCodeRef.h" | 
|  | #include <wtf/Condition.h> | 
|  | #include <wtf/DataLog.h> | 
|  | #include <wtf/Deque.h> | 
|  | #include <wtf/Lock.h> | 
|  | #include <wtf/NeverDestroyed.h> | 
|  | #include <wtf/StringPrintStream.h> | 
|  | #include <wtf/Threading.h> | 
|  |  | 
|  | namespace JSC { | 
|  |  | 
|  | void disassemble(const MacroAssemblerCodePtr<DisassemblyPtrTag>& codePtr, size_t size, const char* prefix, PrintStream& out) | 
|  | { | 
|  | if (tryToDisassemble(codePtr, size, prefix, out)) | 
|  | return; | 
|  |  | 
|  | out.printf("%sdisassembly not available for range %p...%p\n", prefix, codePtr.untaggedExecutableAddress(), codePtr.untaggedExecutableAddress<char*>() + 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<DisassemblyPtrTag> codeRef; | 
|  | size_t size { 0 }; | 
|  | const char* prefix { nullptr }; | 
|  | }; | 
|  |  | 
|  | class AsynchronousDisassembler { | 
|  | public: | 
|  | AsynchronousDisassembler() | 
|  | { | 
|  | Thread::create("Asynchronous Disassembler", [&] () { run(); }); | 
|  | } | 
|  |  | 
|  | void enqueue(std::unique_ptr<DisassemblyTask> task) | 
|  | { | 
|  | LockHolder locker(m_lock); | 
|  | m_queue.append(WTFMove(task)); | 
|  | m_condition.notifyAll(); | 
|  | } | 
|  |  | 
|  | void waitUntilEmpty() | 
|  | { | 
|  | LockHolder 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; | 
|  | { | 
|  | LockHolder locker(m_lock); | 
|  | m_working = false; | 
|  | m_condition.notifyAll(); | 
|  | 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()); | 
|  | } | 
|  | } | 
|  |  | 
|  | Lock m_lock; | 
|  | Condition 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<DisassemblyPtrTag>& codeRef, size_t size, const char* prefix) | 
|  | { | 
|  | std::unique_ptr<DisassemblyTask> task = makeUnique<DisassemblyTask>(); | 
|  | task->header = strdup(header.data()); // Yuck! We need this because CString does racy refcounting. | 
|  | task->codeRef = codeRef; | 
|  | task->size = size; | 
|  | task->prefix = prefix; | 
|  |  | 
|  | asynchronousDisassembler().enqueue(WTFMove(task)); | 
|  | } | 
|  |  | 
|  | void waitForAsynchronousDisassembly() | 
|  | { | 
|  | if (!hadAnyAsynchronousDisassembly) | 
|  | return; | 
|  |  | 
|  | asynchronousDisassembler().waitUntilEmpty(); | 
|  | } | 
|  |  | 
|  | } // namespace JSC | 
|  |  |