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 (PLATFORM(MAC) && 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 PLATFORM(MAC) && 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 (PLATFORM(MAC) && 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 (PLATFORM(MAC) && 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