94ebe8e2ca7e189f8f6d8d5f64d90f9d061c97df
[WebKit-https.git] / Source / JavaScriptCore / tools / CodeProfiling.cpp
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
41 namespace JSC {
42
43 volatile CodeProfile* CodeProfiling::s_profileStack = 0;
44 CodeProfiling::Mode CodeProfiling::s_mode = CodeProfiling::Disabled;
45 WTF::MetaAllocatorTracker* CodeProfiling::s_tracker = 0;
46
47 IGNORE_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.
52 static 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
63 IGNORE_WARNINGS_END
64
65 #if HAVE(MACHINE_CONTEXT)
66 static 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.
78 void 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
85 void 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
115 void* 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
125 void 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
147 void 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 }