| /* |
| * 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 "CodeProfiling.h" |
| |
| #include "CodeProfile.h" |
| #include "MachineContext.h" |
| #include <wtf/MetaAllocator.h> |
| |
| #if HAVE(SIGNAL_H) |
| #include <signal.h> |
| #endif |
| |
| #if HAVE(MACHINE_CONTEXT) |
| #include <sys/time.h> |
| #endif |
| |
| namespace JSC { |
| |
| volatile CodeProfile* CodeProfiling::s_profileStack = 0; |
| CodeProfiling::Mode CodeProfiling::s_mode = CodeProfiling::Disabled; |
| WTF::MetaAllocatorTracker* CodeProfiling::s_tracker = 0; |
| |
| IGNORE_WARNINGS_BEGIN("missing-noreturn") |
| |
| #if HAVE(MACHINE_CONTEXT) |
| // Helper function to start & stop the timer. |
| // Presently we're using the wall-clock timer, since this seems to give the best results. |
| static void setProfileTimer(unsigned usec) |
| { |
| itimerval timer; |
| timer.it_value.tv_sec = 0; |
| timer.it_value.tv_usec = usec; |
| timer.it_interval.tv_sec = 0; |
| timer.it_interval.tv_usec = usec; |
| setitimer(ITIMER_REAL, &timer, 0); |
| } |
| #endif |
| |
| IGNORE_WARNINGS_END |
| |
| #if HAVE(MACHINE_CONTEXT) |
| static void profilingTimer(int, siginfo_t*, void* uap) |
| { |
| PlatformRegisters& platformRegisters = WTF::registersFromUContext(static_cast<ucontext_t*>(uap)); |
| if (auto instructionPointer = MachineContext::instructionPointer(platformRegisters)) { |
| CodeProfiling::sample( |
| instructionPointer->untaggedExecutableAddress(), |
| reinterpret_cast<void**>(MachineContext::framePointer(platformRegisters))); |
| } |
| } |
| #endif |
| |
| // Callback triggered when the timer is fired. |
| void CodeProfiling::sample(void* pc, void** framePointer) |
| { |
| CodeProfile* profileStack = const_cast<CodeProfile*>(s_profileStack); |
| if (profileStack) |
| profileStack->sample(pc, framePointer); |
| } |
| |
| void CodeProfiling::notifyAllocator(WTF::MetaAllocator* allocator) |
| { |
| // Check for JSC_CODE_PROFILING. |
| const char* codeProfilingMode = getenv("JSC_CODE_PROFILING"); |
| if (!codeProfilingMode) |
| return; |
| |
| // Check for a valid mode, currently "1", "2", or "3". |
| if (!codeProfilingMode[0] || codeProfilingMode[1]) |
| return; |
| switch (*codeProfilingMode) { |
| case '1': |
| s_mode = Enabled; |
| break; |
| case '2': |
| s_mode = Verbose; |
| break; |
| case '3': |
| s_mode = VeryVerbose; |
| break; |
| default: |
| return; |
| } |
| |
| ASSERT(enabled()); |
| ASSERT(!s_tracker); |
| s_tracker = new WTF::MetaAllocatorTracker(); |
| allocator->trackAllocations(s_tracker); |
| } |
| |
| void* CodeProfiling::getOwnerUIDForPC(void* address) |
| { |
| if (!s_tracker) |
| return 0; |
| WTF::MetaAllocatorHandle* handle = s_tracker->find(address); |
| if (!handle) |
| return 0; |
| return handle->ownerUID(); |
| } |
| |
| void CodeProfiling::begin(const SourceCode& source) |
| { |
| // Push a new CodeProfile onto the stack for each script encountered. |
| CodeProfile* profileStack = const_cast<CodeProfile*>(s_profileStack); |
| bool alreadyProfiling = profileStack; |
| s_profileStack = profileStack = new CodeProfile(source, profileStack); |
| |
| // Is the profiler already running - if so, the timer will already be set up. |
| if (alreadyProfiling) |
| return; |
| |
| #if HAVE(MACHINE_CONTEXT) |
| // Regsiter a signal handler & itimer. |
| struct sigaction action; |
| action.sa_sigaction = reinterpret_cast<void (*)(int, siginfo_t *, void *)>(profilingTimer); |
| sigfillset(&action.sa_mask); |
| action.sa_flags = SA_SIGINFO; |
| sigaction(SIGALRM, &action, 0); |
| setProfileTimer(100); |
| #endif |
| } |
| |
| void CodeProfiling::end() |
| { |
| // Pop the current profiler off the stack. |
| CodeProfile* current = const_cast<CodeProfile*>(s_profileStack); |
| ASSERT(current); |
| s_profileStack = current->parent(); |
| |
| // Is this the outermost script being profiled? - if not, just return. |
| // We perform all output of profiles recursively from the outermost script, |
| // to minimize profiling overhead from skewing results. |
| if (s_profileStack) |
| return; |
| |
| #if HAVE(MACHINE_CONTEXT) |
| // Stop profiling |
| setProfileTimer(0); |
| #endif |
| |
| current->report(); |
| delete current; |
| } |
| |
| } |