1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Mozilla Foundation.
19 : * Portions created by the Initial Developer are Copyright (C) 2011
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Benoit Girard <bgirard@mozilla.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include <stdlib.h>
40 : #include <signal.h>
41 : #include "thread_helper.h"
42 : #include "nscore.h"
43 : #include "jsapi.h"
44 : #include "mozilla/TimeStamp.h"
45 :
46 : using mozilla::TimeStamp;
47 : using mozilla::TimeDuration;
48 :
49 : extern mozilla::tls::key pkey_stack;
50 : extern mozilla::tls::key pkey_ticker;
51 : extern bool stack_key_initialized;
52 :
53 : #ifndef SAMPLE_FUNCTION_NAME
54 : # ifdef __GNUC__
55 : # define SAMPLE_FUNCTION_NAME __FUNCTION__
56 : # elif defined(_MSC_VER)
57 : # define SAMPLE_FUNCTION_NAME __FUNCTION__
58 : # else
59 : # define SAMPLE_FUNCTION_NAME __func__ // defined in C99, supported in various C++ compilers. Just raw function name.
60 : # endif
61 : #endif
62 :
63 : #define SAMPLER_INIT() mozilla_sampler_init()
64 : #define SAMPLER_DEINIT() mozilla_sampler_deinit()
65 : #define SAMPLER_START(entries, interval, features, featureCount) mozilla_sampler_start(entries, interval, features, featureCount)
66 : #define SAMPLER_STOP() mozilla_sampler_stop()
67 : #define SAMPLER_IS_ACTIVE() mozilla_sampler_is_active()
68 : #define SAMPLER_RESPONSIVENESS(time) mozilla_sampler_responsiveness(time)
69 : #define SAMPLER_GET_RESPONSIVENESS() mozilla_sampler_get_responsiveness()
70 : #define SAMPLER_SAVE() mozilla_sampler_save()
71 : #define SAMPLER_GET_PROFILE() mozilla_sampler_get_profile()
72 : #define SAMPLER_GET_PROFILE_DATA(ctx) mozilla_sampler_get_profile_data(ctx)
73 : #define SAMPLER_GET_FEATURES() mozilla_sampler_get_features()
74 : // we want the class and function name but can't easily get that using preprocessor macros
75 : // __func__ doesn't have the class name and __PRETTY_FUNCTION__ has the parameters
76 : #define SAMPLE_LABEL(name_space, info) mozilla::SamplerStackFrameRAII only_one_sampleraii_per_scope(name_space "::" info)
77 : #define SAMPLE_MARKER(info) mozilla_sampler_add_marker(info)
78 :
79 : /* we duplicate this code here to avoid header dependencies
80 : * which make it more difficult to include in other places */
81 : #if defined(_M_X64) || defined(__x86_64__)
82 : #define V8_HOST_ARCH_X64 1
83 : #elif defined(_M_IX86) || defined(__i386__) || defined(__i386)
84 : #define V8_HOST_ARCH_IA32 1
85 : #elif defined(__ARMEL__)
86 : #define V8_HOST_ARCH_ARM 1
87 : #else
88 : #warning Please add support for your architecture in chromium_types.h
89 : #endif
90 :
91 :
92 : // STORE_SEQUENCER: Because signals can interrupt our profile modification
93 : // we need to make stores are not re-ordered by the compiler
94 : // or hardware to make sure the profile is consistent at
95 : // every point the signal can fire.
96 : #ifdef V8_HOST_ARCH_ARM
97 : // TODO Is there something cheaper that will prevent
98 : // memory stores from being reordered
99 :
100 : typedef void (*LinuxKernelMemoryBarrierFunc)(void);
101 : LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) =
102 : (LinuxKernelMemoryBarrierFunc) 0xffff0fa0;
103 :
104 : # define STORE_SEQUENCER() pLinuxKernelMemoryBarrier()
105 : #elif defined(V8_HOST_ARCH_IA32) || defined(V8_HOST_ARCH_X64)
106 : # if defined(_MSC_VER)
107 : // MSVC2005 has a name collision bug caused when both <intrin.h> and <winnt.h> are included together.
108 : #ifdef _WINNT_
109 : # define _interlockedbittestandreset _interlockedbittestandreset_NAME_CHANGED_TO_AVOID_MSVS2005_ERROR
110 : # define _interlockedbittestandset _interlockedbittestandset_NAME_CHANGED_TO_AVOID_MSVS2005_ERROR
111 : # include <intrin.h>
112 : #else
113 : # include <intrin.h>
114 : # define _interlockedbittestandreset _interlockedbittestandreset_NAME_CHANGED_TO_AVOID_MSVS2005_ERROR
115 : # define _interlockedbittestandset _interlockedbittestandset_NAME_CHANGED_TO_AVOID_MSVS2005_ERROR
116 : #endif
117 : // Even though MSVC2005 has the intrinsic _ReadWriteBarrier, it fails to link to it when it's
118 : // not explicitly declared.
119 : # pragma intrinsic(_ReadWriteBarrier)
120 : # define STORE_SEQUENCER() _ReadWriteBarrier();
121 : # elif defined(__INTEL_COMPILER)
122 : # define STORE_SEQUENCER() __memory_barrier();
123 : # elif __GNUC__
124 : # define STORE_SEQUENCER() asm volatile("" ::: "memory");
125 : # else
126 : # error "Memory clobber not supported for your compiler."
127 : # endif
128 : #else
129 : # error "Memory clobber not supported for your platform."
130 : #endif
131 :
132 : // Returns a handdle to pass on exit. This can check that we are popping the
133 : // correct callstack.
134 : inline void* mozilla_sampler_call_enter(const char *aInfo);
135 : inline void mozilla_sampler_call_exit(void* handle);
136 : inline void mozilla_sampler_add_marker(const char *aInfo);
137 :
138 : void mozilla_sampler_start(int aEntries, int aInterval, const char** aFeatures, uint32_t aFeatureCount);
139 : void mozilla_sampler_stop();
140 : bool mozilla_sampler_is_active();
141 : void mozilla_sampler_responsiveness(TimeStamp time);
142 : const double* mozilla_sampler_get_responsiveness();
143 : void mozilla_sampler_save();
144 : char* mozilla_sampler_get_profile();
145 : JSObject *mozilla_sampler_get_profile_data(JSContext *aCx);
146 : const char** mozilla_sampler_get_features();
147 : void mozilla_sampler_init();
148 :
149 : namespace mozilla {
150 :
151 : class NS_STACK_CLASS SamplerStackFrameRAII {
152 : public:
153 : // we only copy the strings at save time, so to take multiple parameters we'd need to copy them then.
154 184079 : SamplerStackFrameRAII(const char *aInfo) {
155 184079 : mHandle = mozilla_sampler_call_enter(aInfo);
156 184079 : }
157 184079 : ~SamplerStackFrameRAII() {
158 184079 : mozilla_sampler_call_exit(mHandle);
159 184079 : }
160 : private:
161 : void* mHandle;
162 : };
163 :
164 : } //mozilla
165 :
166 : // the SamplerStack members are read by signal
167 : // handlers, so the mutation of them needs to be signal-safe.
168 : struct ProfileStack
169 : {
170 : public:
171 : ProfileStack()
172 : : mStackPointer(0)
173 : , mMarkerPointer(0)
174 : , mDroppedStackEntries(0)
175 : , mQueueClearMarker(false)
176 : { }
177 :
178 : void addMarker(const char *aMarker)
179 : {
180 : if (mQueueClearMarker) {
181 : clearMarkers();
182 : }
183 : if (!aMarker) {
184 : return; //discard
185 : }
186 : if (mMarkerPointer == 1024) {
187 : return; //array full, silently drop
188 : }
189 : mMarkers[mMarkerPointer] = aMarker;
190 : STORE_SEQUENCER();
191 : mMarkerPointer++;
192 : }
193 :
194 : // called within signal. Function must be reentrant
195 : const char* getMarker(int aMarkerId)
196 : {
197 : if (mQueueClearMarker) {
198 : clearMarkers();
199 : }
200 : if (aMarkerId >= mMarkerPointer) {
201 : return NULL;
202 : }
203 : return mMarkers[aMarkerId];
204 : }
205 :
206 : // called within signal. Function must be reentrant
207 : void clearMarkers()
208 : {
209 : mMarkerPointer = 0;
210 : mQueueClearMarker = false;
211 : }
212 :
213 23 : void push(const char *aName)
214 : {
215 23 : if (mStackPointer >= 1024) {
216 0 : mDroppedStackEntries++;
217 0 : return;
218 : }
219 :
220 : // Make sure we increment the pointer after the name has
221 : // been written such that mStack is always consistent.
222 23 : mStack[mStackPointer] = aName;
223 : // Prevent the optimizer from re-ordering these instructions
224 23 : STORE_SEQUENCER();
225 23 : mStackPointer++;
226 : }
227 23 : void pop()
228 : {
229 23 : if (mDroppedStackEntries > 0) {
230 0 : mDroppedStackEntries--;
231 : } else {
232 23 : mStackPointer--;
233 : }
234 23 : }
235 : bool isEmpty()
236 : {
237 : return mStackPointer == 0;
238 : }
239 :
240 : // Keep a list of active checkpoints
241 : char const * volatile mStack[1024];
242 : // Keep a list of active markers to be applied to the next sample taken
243 : char const * volatile mMarkers[1024];
244 : volatile mozilla::sig_safe_t mStackPointer;
245 : volatile mozilla::sig_safe_t mMarkerPointer;
246 : volatile mozilla::sig_safe_t mDroppedStackEntries;
247 : // We don't want to modify _markers from within the signal so we allow
248 : // it to queue a clear operation.
249 : volatile mozilla::sig_safe_t mQueueClearMarker;
250 : };
251 :
252 184079 : inline void* mozilla_sampler_call_enter(const char *aInfo)
253 : {
254 : // check if we've been initialized to avoid calling pthread_getspecific
255 : // with a null pkey_stack which will return undefined results.
256 184079 : if (!stack_key_initialized)
257 184056 : return NULL;
258 :
259 23 : ProfileStack *stack = mozilla::tls::get<ProfileStack>(pkey_stack);
260 : // we can't infer whether 'stack' has been initialized
261 : // based on the value of stack_key_intiailized because
262 : // 'stack' is only intialized when a thread is being
263 : // profiled.
264 23 : if (!stack) {
265 0 : return stack;
266 : }
267 23 : stack->push(aInfo);
268 :
269 : // The handle is meant to support future changes
270 : // but for now it is simply use to save a call to
271 : // pthread_getspecific on exit. It also supports the
272 : // case where the sampler is initialized between
273 : // enter and exit.
274 23 : return stack;
275 : }
276 :
277 184079 : inline void mozilla_sampler_call_exit(void *aHandle)
278 : {
279 184079 : if (!aHandle)
280 184056 : return;
281 :
282 23 : ProfileStack *stack = (ProfileStack*)aHandle;
283 23 : stack->pop();
284 : }
285 :
286 : inline void mozilla_sampler_add_marker(const char *aMarker)
287 : {
288 : ProfileStack *stack = mozilla::tls::get<ProfileStack>(pkey_stack);
289 : if (!stack) {
290 : return;
291 : }
292 : stack->addMarker(aMarker);
293 : }
294 :
|