1/*
2 * Copyright (C) 2012-2018 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 "MachineContext.h"
31#include <wtf/MetaAllocator.h>
32
33#if HAVE(SIGNAL_H)
34#include <signal.h>
35#endif
36
37#if HAVE(MACHINE_CONTEXT)
38#include <sys/time.h>
39#endif
40
41namespace JSC {
42
43volatile CodeProfile* CodeProfiling::s_profileStack = 0;
44CodeProfiling::Mode CodeProfiling::s_mode = CodeProfiling::Disabled;
45WTF::MetaAllocatorTracker* CodeProfiling::s_tracker = 0;
46
47IGNORE_WARNINGS_BEGIN("missing-noreturn")
48
49#if HAVE(MACHINE_CONTEXT)
50// Helper function to start & stop the timer.
51// Presently we're using the wall-clock timer, since this seems to give the best results.
52static void setProfileTimer(unsigned usec)
53{
54 itimerval timer;
55 timer.it_value.tv_sec = 0;
56 timer.it_value.tv_usec = usec;
57 timer.it_interval.tv_sec = 0;
58 timer.it_interval.tv_usec = usec;
59 setitimer(ITIMER_REAL, &timer, 0);
60}
61#endif
62
63IGNORE_WARNINGS_END
64
65#if HAVE(MACHINE_CONTEXT)
66static void profilingTimer(int, siginfo_t*, void* uap)
67{
68 PlatformRegisters& platformRegisters = WTF::registersFromUContext(static_cast<ucontext_t*>(uap));
69 if (auto instructionPointer = MachineContext::instructionPointer(platformRegisters)) {
70 CodeProfiling::sample(
71 instructionPointer->untaggedExecutableAddress(),
72 reinterpret_cast<void**>(MachineContext::framePointer(platformRegisters)));
73 }
74}
75#endif
76
77// Callback triggered when the timer is fired.
78void CodeProfiling::sample(void* pc, void** framePointer)
79{
80 CodeProfile* profileStack = const_cast<CodeProfile*>(s_profileStack);
81 if (profileStack)
82 profileStack->sample(pc, framePointer);
83}
84
85void CodeProfiling::notifyAllocator(WTF::MetaAllocator* allocator)
86{
87 // Check for JSC_CODE_PROFILING.
88 const char* codeProfilingMode = getenv("JSC_CODE_PROFILING");
89 if (!codeProfilingMode)
90 return;
91
92 // Check for a valid mode, currently "1", "2", or "3".
93 if (!codeProfilingMode[0] || codeProfilingMode[1])
94 return;
95 switch (*codeProfilingMode) {
96 case '1':
97 s_mode = Enabled;
98 break;
99 case '2':
100 s_mode = Verbose;
101 break;
102 case '3':
103 s_mode = VeryVerbose;
104 break;
105 default:
106 return;
107 }
108
109 ASSERT(enabled());
110 ASSERT(!s_tracker);
111 s_tracker = new WTF::MetaAllocatorTracker();
112 allocator->trackAllocations(s_tracker);
113}
114
115void* CodeProfiling::getOwnerUIDForPC(void* address)
116{
117 if (!s_tracker)
118 return 0;
119 WTF::MetaAllocatorHandle* handle = s_tracker->find(address);
120 if (!handle)
121 return 0;
122 return handle->ownerUID();
123}
124
125void CodeProfiling::begin(const SourceCode& source)
126{
127 // Push a new CodeProfile onto the stack for each script encountered.
128 CodeProfile* profileStack = const_cast<CodeProfile*>(s_profileStack);
129 bool alreadyProfiling = profileStack;
130 s_profileStack = profileStack = new CodeProfile(source, profileStack);
131
132 // Is the profiler already running - if so, the timer will already be set up.
133 if (alreadyProfiling)
134 return;
135
136#if HAVE(MACHINE_CONTEXT)
137 // Regsiter a signal handler & itimer.
138 struct sigaction action;
139 action.sa_sigaction = reinterpret_cast<void (*)(int, siginfo_t *, void *)>(profilingTimer);
140 sigfillset(&action.sa_mask);
141 action.sa_flags = SA_SIGINFO;
142 sigaction(SIGALRM, &action, 0);
143 setProfileTimer(100);
144#endif
145}
146
147void CodeProfiling::end()
148{
149 // Pop the current profiler off the stack.
150 CodeProfile* current = const_cast<CodeProfile*>(s_profileStack);
151 ASSERT(current);
152 s_profileStack = current->parent();
153
154 // Is this the outermost script being profiled? - if not, just return.
155 // We perform all output of profiles recursively from the outermost script,
156 // to minimize profiling overhead from skewing results.
157 if (s_profileStack)
158 return;
159
160#if HAVE(MACHINE_CONTEXT)
161 // Stop profiling
162 setProfileTimer(0);
163#endif
164
165 current->report();
166 delete current;
167}
168
169}
170