/* * Copyright (C) 2012 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 #if HAVE(SIGNAL_H) #include #endif #if OS(LINUX) || OS(DARWIN) #include #endif namespace JSC { volatile CodeProfile* CodeProfiling::s_profileStack = 0; CodeProfiling::Mode CodeProfiling::s_mode = CodeProfiling::Disabled; WTF::MetaAllocatorTracker* CodeProfiling::s_tracker = 0; #if COMPILER(CLANG) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmissing-noreturn" #endif #if (OS(DARWIN) && !PLATFORM(EFL) && !PLATFORM(GTK) && CPU(X86_64)) || (OS(LINUX) && CPU(X86)) // 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 #if COMPILER(CLANG) #pragma clang diagnostic pop #endif #if OS(DARWIN) && !PLATFORM(EFL) && !PLATFORM(GTK) && CPU(X86_64) static void profilingTimer(int, siginfo_t*, void* uap) { mcontext_t context = static_cast(uap)->uc_mcontext; CodeProfiling::sample(reinterpret_cast(context->__ss.__rip), reinterpret_cast(context->__ss.__rbp)); } #elif OS(LINUX) && CPU(X86) static void profilingTimer(int, siginfo_t*, void* uap) { mcontext_t context = static_cast(uap)->uc_mcontext; CodeProfiling::sample(reinterpret_cast(context.gregs[REG_EIP]), reinterpret_cast(context.gregs[REG_EBP])); } #endif // Callback triggered when the timer is fired. void CodeProfiling::sample(void* pc, void** framePointer) { CodeProfile* profileStack = const_cast(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(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 (OS(DARWIN) && !PLATFORM(EFL) && !PLATFORM(GTK) && CPU(X86_64)) || (OS(LINUX) && CPU(X86)) // Regsiter a signal handler & itimer. struct sigaction action; action.sa_sigaction = reinterpret_cast(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(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 (OS(DARWIN) && !PLATFORM(EFL) && !PLATFORM(GTK) && CPU(X86_64)) || (OS(LINUX) && CPU(X86)) // Stop profiling setProfileTimer(0); #endif current->report(); delete current; } }