1/* 2 * Copyright (C) 2012 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "CodeProfiling.h" 28 29#include "CodeProfile.h" 30#include <wtf/MetaAllocator.h> 31 32#if HAVE(SIGNAL_H) 33#include <signal.h> 34#endif 35 36#if OS(LINUX) 37#include <sys/time.h> 38#endif 39 40namespace JSC { 41 42volatile CodeProfile* CodeProfiling::s_profileStack = 0; 43CodeProfiling::Mode CodeProfiling::s_mode = CodeProfiling::Disabled; 44WTF::MetaAllocatorTracker* CodeProfiling::s_tracker = 0; 45 46#if COMPILER(CLANG) 47#pragma clang diagnostic push 48#pragma clang diagnostic ignored "-Wmissing-noreturn" 49#endif 50 51#if (OS(DARWIN) && !PLATFORM(EFL) && !PLATFORM(GTK) && CPU(X86_64)) || (OS(LINUX) && CPU(X86)) 52// Helper function to start & stop the timer. 53// Presently we're using the wall-clock timer, since this seems to give the best results. 54static void setProfileTimer(unsigned usec) 55{ 56 itimerval timer; 57 timer.it_value.tv_sec = 0; 58 timer.it_value.tv_usec = usec; 59 timer.it_interval.tv_sec = 0; 60 timer.it_interval.tv_usec = usec; 61 setitimer(ITIMER_REAL, &timer, 0); 62} 63#endif 64 65#if COMPILER(CLANG) 66#pragma clang diagnostic pop 67#endif 68 69#if OS(DARWIN) && !PLATFORM(EFL) && !PLATFORM(GTK) && CPU(X86_64) 70static void profilingTimer(int, siginfo_t*, void* uap) 71{ 72 mcontext_t context = static_cast<ucontext_t*>(uap)->uc_mcontext; 73 CodeProfiling::sample(reinterpret_cast<void*>(context->__ss.__rip), 74 reinterpret_cast<void**>(context->__ss.__rbp)); 75} 76#elif OS(LINUX) && CPU(X86) 77static void profilingTimer(int, siginfo_t*, void* uap) 78{ 79 mcontext_t context = static_cast<ucontext_t*>(uap)->uc_mcontext; 80 CodeProfiling::sample(reinterpret_cast<void*>(context.gregs[REG_EIP]), 81 reinterpret_cast<void**>(context.gregs[REG_EBP])); 82} 83#endif 84 85// Callback triggered when the timer is fired. 86void CodeProfiling::sample(void* pc, void** framePointer) 87{ 88 CodeProfile* profileStack = const_cast<CodeProfile*>(s_profileStack); 89 if (profileStack) 90 profileStack->sample(pc, framePointer); 91} 92 93void CodeProfiling::notifyAllocator(WTF::MetaAllocator* allocator) 94{ 95#if !OS(WINCE) 96 // Check for JSC_CODE_PROFILING. 97 const char* codeProfilingMode = getenv("JSC_CODE_PROFILING"); 98 if (!codeProfilingMode) 99 return; 100 101 // Check for a valid mode, currently "1", "2", or "3". 102 if (!codeProfilingMode[0] || codeProfilingMode[1]) 103 return; 104 switch (*codeProfilingMode) { 105 case '1': 106 s_mode = Enabled; 107 break; 108 case '2': 109 s_mode = Verbose; 110 break; 111 case '3': 112 s_mode = VeryVerbose; 113 break; 114 default: 115 return; 116 } 117 118 ASSERT(enabled()); 119 ASSERT(!s_tracker); 120 s_tracker = new WTF::MetaAllocatorTracker(); 121 allocator->trackAllocations(s_tracker); 122#endif 123} 124 125void* CodeProfiling::getOwnerUIDForPC(void* address) 126{ 127 if (!s_tracker) 128 return 0; 129 WTF::MetaAllocatorHandle* handle = s_tracker->find(address); 130 if (!handle) 131 return 0; 132 return handle->ownerUID(); 133} 134 135void CodeProfiling::begin(const SourceCode& source) 136{ 137 // Push a new CodeProfile onto the stack for each script encountered. 138 CodeProfile* profileStack = const_cast<CodeProfile*>(s_profileStack); 139 bool alreadyProfiling = profileStack; 140 s_profileStack = profileStack = new CodeProfile(source, profileStack); 141 142 // Is the profiler already running - if so, the timer will already be set up. 143 if (alreadyProfiling) 144 return; 145 146#if (OS(DARWIN) && !PLATFORM(EFL) && !PLATFORM(GTK) && CPU(X86_64)) || (OS(LINUX) && CPU(X86)) 147 // Regsiter a signal handler & itimer. 148 struct sigaction action; 149 action.sa_sigaction = reinterpret_cast<void (*)(int, siginfo_t *, void *)>(profilingTimer); 150 sigfillset(&action.sa_mask); 151 action.sa_flags = SA_SIGINFO; 152 sigaction(SIGALRM, &action, 0); 153 setProfileTimer(100); 154#endif 155} 156 157void CodeProfiling::end() 158{ 159 // Pop the current profiler off the stack. 160 CodeProfile* current = const_cast<CodeProfile*>(s_profileStack); 161 ASSERT(current); 162 s_profileStack = current->parent(); 163 164 // Is this the outermost script being profiled? - if not, just return. 165 // We perform all output of profiles recursively from the outermost script, 166 // to minimize profiling overhead from skewing results. 167 if (s_profileStack) 168 return; 169 170#if (OS(DARWIN) && !PLATFORM(EFL) && !PLATFORM(GTK) && CPU(X86_64)) || (OS(LINUX) && CPU(X86)) 171 // Stop profiling 172 setProfileTimer(0); 173#endif 174 175 current->report(); 176 delete current; 177} 178 179} 180