|
1 // Copyright (c) 2006-2011 The Chromium Authors. All rights reserved. |
|
2 // |
|
3 // Redistribution and use in source and binary forms, with or without |
|
4 // modification, are permitted provided that the following conditions |
|
5 // are met: |
|
6 // * Redistributions of source code must retain the above copyright |
|
7 // notice, this list of conditions and the following disclaimer. |
|
8 // * Redistributions in binary form must reproduce the above copyright |
|
9 // notice, this list of conditions and the following disclaimer in |
|
10 // the documentation and/or other materials provided with the |
|
11 // distribution. |
|
12 // * Neither the name of Google, Inc. nor the names of its contributors |
|
13 // may be used to endorse or promote products derived from this |
|
14 // software without specific prior written permission. |
|
15 // |
|
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
|
19 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
|
20 // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
21 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
|
22 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS |
|
23 // OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
|
24 // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
|
25 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
|
26 // OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
27 // SUCH DAMAGE. |
|
28 |
|
29 #ifndef TOOLS_PLATFORM_H_ |
|
30 #define TOOLS_PLATFORM_H_ |
|
31 |
|
32 #ifdef ANDROID |
|
33 #include <android/log.h> |
|
34 #else |
|
35 #define __android_log_print(a, ...) |
|
36 #endif |
|
37 |
|
38 #ifdef XP_UNIX |
|
39 #include <pthread.h> |
|
40 #endif |
|
41 |
|
42 #include <stdint.h> |
|
43 #include <math.h> |
|
44 #include "mozilla/unused.h" |
|
45 #include "mozilla/TimeStamp.h" |
|
46 #include "mozilla/Mutex.h" |
|
47 #include "MainThreadUtils.h" |
|
48 #include "PlatformMacros.h" |
|
49 #include "v8-support.h" |
|
50 #include <vector> |
|
51 |
|
52 #ifdef XP_WIN |
|
53 #include <windows.h> |
|
54 #endif |
|
55 |
|
56 #define ASSERT(a) MOZ_ASSERT(a) |
|
57 |
|
58 bool moz_profiler_verbose(); |
|
59 |
|
60 #ifdef ANDROID |
|
61 # if defined(__arm__) || defined(__thumb__) |
|
62 # define ENABLE_SPS_LEAF_DATA |
|
63 # define ENABLE_ARM_LR_SAVING |
|
64 # endif |
|
65 # define LOG(text) \ |
|
66 do { if (moz_profiler_verbose()) \ |
|
67 __android_log_write(ANDROID_LOG_ERROR, "Profiler", text); \ |
|
68 } while (0) |
|
69 # define LOGF(format, ...) \ |
|
70 do { if (moz_profiler_verbose()) \ |
|
71 __android_log_print(ANDROID_LOG_ERROR, "Profiler", format, \ |
|
72 __VA_ARGS__); \ |
|
73 } while (0) |
|
74 |
|
75 #else |
|
76 # define LOG(text) \ |
|
77 do { if (moz_profiler_verbose()) fprintf(stderr, "Profiler: %s\n", text); \ |
|
78 } while (0) |
|
79 # define LOGF(format, ...) \ |
|
80 do { if (moz_profiler_verbose()) fprintf(stderr, "Profiler: " format \ |
|
81 "\n", __VA_ARGS__); \ |
|
82 } while (0) |
|
83 |
|
84 #endif |
|
85 |
|
86 #if defined(XP_MACOSX) || defined(XP_WIN) || defined(XP_LINUX) |
|
87 #define ENABLE_SPS_LEAF_DATA |
|
88 #endif |
|
89 |
|
90 extern mozilla::TimeStamp sStartTime; |
|
91 |
|
92 typedef uint8_t* Address; |
|
93 |
|
94 // ---------------------------------------------------------------------------- |
|
95 // Mutex |
|
96 // |
|
97 // Mutexes are used for serializing access to non-reentrant sections of code. |
|
98 // The implementations of mutex should allow for nested/recursive locking. |
|
99 |
|
100 class Mutex { |
|
101 public: |
|
102 virtual ~Mutex() {} |
|
103 |
|
104 // Locks the given mutex. If the mutex is currently unlocked, it becomes |
|
105 // locked and owned by the calling thread, and immediately. If the mutex |
|
106 // is already locked by another thread, suspends the calling thread until |
|
107 // the mutex is unlocked. |
|
108 virtual int Lock() = 0; |
|
109 |
|
110 // Unlocks the given mutex. The mutex is assumed to be locked and owned by |
|
111 // the calling thread on entrance. |
|
112 virtual int Unlock() = 0; |
|
113 |
|
114 // Tries to lock the given mutex. Returns whether the mutex was |
|
115 // successfully locked. |
|
116 virtual bool TryLock() = 0; |
|
117 }; |
|
118 |
|
119 // ---------------------------------------------------------------------------- |
|
120 // OS |
|
121 // |
|
122 // This class has static methods for the different platform specific |
|
123 // functions. Add methods here to cope with differences between the |
|
124 // supported platforms. |
|
125 |
|
126 class OS { |
|
127 public: |
|
128 |
|
129 // Sleep for a number of milliseconds. |
|
130 static void Sleep(const int milliseconds); |
|
131 |
|
132 // Sleep for a number of microseconds. |
|
133 static void SleepMicro(const int microseconds); |
|
134 |
|
135 // Called on startup to initialize platform specific things |
|
136 static void Startup(); |
|
137 |
|
138 private: |
|
139 static const int msPerSecond = 1000; |
|
140 |
|
141 }; |
|
142 |
|
143 |
|
144 |
|
145 |
|
146 // ---------------------------------------------------------------------------- |
|
147 // Thread |
|
148 // |
|
149 // Thread objects are used for creating and running threads. When the start() |
|
150 // method is called the new thread starts running the run() method in the new |
|
151 // thread. The Thread object should not be deallocated before the thread has |
|
152 // terminated. |
|
153 |
|
154 class Thread { |
|
155 public: |
|
156 // Create new thread. |
|
157 explicit Thread(const char* name); |
|
158 virtual ~Thread(); |
|
159 |
|
160 // Start new thread by calling the Run() method in the new thread. |
|
161 void Start(); |
|
162 |
|
163 void Join(); |
|
164 |
|
165 inline const char* name() const { |
|
166 return name_; |
|
167 } |
|
168 |
|
169 // Abstract method for run handler. |
|
170 virtual void Run() = 0; |
|
171 |
|
172 // The thread name length is limited to 16 based on Linux's implementation of |
|
173 // prctl(). |
|
174 static const int kMaxThreadNameLength = 16; |
|
175 |
|
176 #ifdef XP_WIN |
|
177 HANDLE thread_; |
|
178 typedef DWORD tid_t; |
|
179 tid_t thread_id_; |
|
180 #else |
|
181 typedef ::pid_t tid_t; |
|
182 #endif |
|
183 #if defined(XP_MACOSX) |
|
184 pthread_t thread_; |
|
185 #endif |
|
186 |
|
187 static tid_t GetCurrentId(); |
|
188 |
|
189 private: |
|
190 void set_name(const char *name); |
|
191 |
|
192 char name_[kMaxThreadNameLength]; |
|
193 int stack_size_; |
|
194 |
|
195 DISALLOW_COPY_AND_ASSIGN(Thread); |
|
196 }; |
|
197 |
|
198 // ---------------------------------------------------------------------------- |
|
199 // HAVE_NATIVE_UNWIND |
|
200 // |
|
201 // Pseudo backtraces are available on all platforms. Native |
|
202 // backtraces are available only on selected platforms. Breakpad is |
|
203 // the only supported native unwinder. HAVE_NATIVE_UNWIND is set at |
|
204 // build time to indicate whether native unwinding is possible on this |
|
205 // platform. The actual unwind mode currently in use is stored in |
|
206 // sUnwindMode. |
|
207 |
|
208 #undef HAVE_NATIVE_UNWIND |
|
209 #if defined(MOZ_PROFILING) \ |
|
210 && (defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_arm_android) \ |
|
211 || defined(SPS_PLAT_x86_linux) \ |
|
212 || defined(SPS_OS_windows) \ |
|
213 || defined(SPS_OS_darwin)) |
|
214 # define HAVE_NATIVE_UNWIND |
|
215 #endif |
|
216 |
|
217 /* Some values extracted at startup from environment variables, that |
|
218 control the behaviour of the breakpad unwinder. */ |
|
219 extern const char* PROFILER_MODE; |
|
220 extern const char* PROFILER_INTERVAL; |
|
221 extern const char* PROFILER_ENTRIES; |
|
222 extern const char* PROFILER_STACK; |
|
223 extern const char* PROFILER_FEATURES; |
|
224 |
|
225 void read_profiler_env_vars(); |
|
226 void profiler_usage(); |
|
227 |
|
228 // Helper methods to expose modifying profiler behavior |
|
229 bool set_profiler_mode(const char*); |
|
230 bool set_profiler_interval(const char*); |
|
231 bool set_profiler_entries(const char*); |
|
232 bool set_profiler_scan(const char*); |
|
233 bool is_native_unwinding_avail(); |
|
234 |
|
235 typedef enum { UnwINVALID, UnwNATIVE, UnwPSEUDO, UnwCOMBINED } UnwMode; |
|
236 extern UnwMode sUnwindMode; /* what mode? */ |
|
237 extern int sUnwindInterval; /* in milliseconds */ |
|
238 extern int sUnwindStackScan; /* max # of dubious frames allowed */ |
|
239 |
|
240 extern int sProfileEntries; /* how many entries do we store? */ |
|
241 |
|
242 void set_tls_stack_top(void* stackTop); |
|
243 |
|
244 // ---------------------------------------------------------------------------- |
|
245 // Sampler |
|
246 // |
|
247 // A sampler periodically samples the state of the VM and optionally |
|
248 // (if used for profiling) the program counter and stack pointer for |
|
249 // the thread that created it. |
|
250 |
|
251 class PseudoStack; |
|
252 class ThreadProfile; |
|
253 |
|
254 // TickSample captures the information collected for each sample. |
|
255 class TickSample { |
|
256 public: |
|
257 TickSample() |
|
258 : |
|
259 pc(NULL), |
|
260 sp(NULL), |
|
261 fp(NULL), |
|
262 #ifdef ENABLE_ARM_LR_SAVING |
|
263 lr(NULL), |
|
264 #endif |
|
265 context(NULL), |
|
266 isSamplingCurrentThread(false) {} |
|
267 |
|
268 void PopulateContext(void* aContext); |
|
269 |
|
270 Address pc; // Instruction pointer. |
|
271 Address sp; // Stack pointer. |
|
272 Address fp; // Frame pointer. |
|
273 #ifdef ENABLE_ARM_LR_SAVING |
|
274 Address lr; // ARM link register |
|
275 #endif |
|
276 void* context; // The context from the signal handler, if available. On |
|
277 // Win32 this may contain the windows thread context. |
|
278 bool isSamplingCurrentThread; |
|
279 ThreadProfile* threadProfile; |
|
280 mozilla::TimeStamp timestamp; |
|
281 }; |
|
282 |
|
283 class ThreadInfo; |
|
284 class PlatformData; |
|
285 class TableTicker; |
|
286 class SyncProfile; |
|
287 class Sampler { |
|
288 public: |
|
289 // Initialize sampler. |
|
290 explicit Sampler(double interval, bool profiling, int entrySize); |
|
291 virtual ~Sampler(); |
|
292 |
|
293 double interval() const { return interval_; } |
|
294 |
|
295 // This method is called for each sampling period with the current |
|
296 // program counter. |
|
297 virtual void Tick(TickSample* sample) = 0; |
|
298 |
|
299 // Immediately captures the calling thread's call stack and returns it. |
|
300 virtual SyncProfile* GetBacktrace() = 0; |
|
301 |
|
302 // Request a save from a signal handler |
|
303 virtual void RequestSave() = 0; |
|
304 // Process any outstanding request outside a signal handler. |
|
305 virtual void HandleSaveRequest() = 0; |
|
306 |
|
307 // Start and stop sampler. |
|
308 void Start(); |
|
309 void Stop(); |
|
310 |
|
311 // Is the sampler used for profiling? |
|
312 bool IsProfiling() const { return profiling_; } |
|
313 |
|
314 // Whether the sampler is running (that is, consumes resources). |
|
315 bool IsActive() const { return active_; } |
|
316 |
|
317 // Low overhead way to stop the sampler from ticking |
|
318 bool IsPaused() const { return paused_; } |
|
319 void SetPaused(bool value) { NoBarrier_Store(&paused_, value); } |
|
320 |
|
321 virtual bool ProfileThreads() const = 0; |
|
322 |
|
323 int EntrySize() { return entrySize_; } |
|
324 |
|
325 // We can't new/delete the type safely without defining it |
|
326 // (-Wdelete-incomplete). Use these Alloc/Free functions instead. |
|
327 static PlatformData* AllocPlatformData(int aThreadId); |
|
328 static void FreePlatformData(PlatformData*); |
|
329 |
|
330 // If we move the backtracing code into the platform files we won't |
|
331 // need to have these hacks |
|
332 #ifdef XP_WIN |
|
333 // xxxehsan sucky hack :( |
|
334 static uintptr_t GetThreadHandle(PlatformData*); |
|
335 #endif |
|
336 #ifdef XP_MACOSX |
|
337 static pthread_t GetProfiledThread(PlatformData*); |
|
338 #endif |
|
339 |
|
340 static std::vector<ThreadInfo*> GetRegisteredThreads() { |
|
341 return *sRegisteredThreads; |
|
342 } |
|
343 |
|
344 static bool RegisterCurrentThread(const char* aName, |
|
345 PseudoStack* aPseudoStack, |
|
346 bool aIsMainThread, void* stackTop); |
|
347 static void UnregisterCurrentThread(); |
|
348 |
|
349 static void Startup(); |
|
350 // Should only be called on shutdown |
|
351 static void Shutdown(); |
|
352 |
|
353 static TableTicker* GetActiveSampler() { return sActiveSampler; } |
|
354 static void SetActiveSampler(TableTicker* sampler) { sActiveSampler = sampler; } |
|
355 |
|
356 static mozilla::Mutex* sRegisteredThreadsMutex; |
|
357 |
|
358 static bool CanNotifyObservers() { |
|
359 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) |
|
360 // Android ANR reporter uses the profiler off the main thread |
|
361 return NS_IsMainThread(); |
|
362 #else |
|
363 MOZ_ASSERT(NS_IsMainThread()); |
|
364 return true; |
|
365 #endif |
|
366 } |
|
367 |
|
368 protected: |
|
369 static std::vector<ThreadInfo*>* sRegisteredThreads; |
|
370 static TableTicker* sActiveSampler; |
|
371 |
|
372 private: |
|
373 void SetActive(bool value) { NoBarrier_Store(&active_, value); } |
|
374 |
|
375 const double interval_; |
|
376 const bool profiling_; |
|
377 Atomic32 paused_; |
|
378 Atomic32 active_; |
|
379 const int entrySize_; |
|
380 |
|
381 // Refactor me! |
|
382 #if defined(SPS_OS_linux) || defined(SPS_OS_android) |
|
383 bool signal_handler_installed_; |
|
384 struct sigaction old_sigprof_signal_handler_; |
|
385 struct sigaction old_sigsave_signal_handler_; |
|
386 bool signal_sender_launched_; |
|
387 pthread_t signal_sender_thread_; |
|
388 #endif |
|
389 }; |
|
390 |
|
391 class ThreadInfo { |
|
392 public: |
|
393 ThreadInfo(const char* aName, int aThreadId, bool aIsMainThread, PseudoStack* aPseudoStack, void* aStackTop) |
|
394 : mName(strdup(aName)) |
|
395 , mThreadId(aThreadId) |
|
396 , mIsMainThread(aIsMainThread) |
|
397 , mPseudoStack(aPseudoStack) |
|
398 , mPlatformData(Sampler::AllocPlatformData(aThreadId)) |
|
399 , mProfile(NULL) |
|
400 , mStackTop(aStackTop) {} |
|
401 |
|
402 virtual ~ThreadInfo(); |
|
403 |
|
404 const char* Name() const { return mName; } |
|
405 int ThreadId() const { return mThreadId; } |
|
406 |
|
407 bool IsMainThread() const { return mIsMainThread; } |
|
408 PseudoStack* Stack() const { return mPseudoStack; } |
|
409 |
|
410 void SetProfile(ThreadProfile* aProfile) { mProfile = aProfile; } |
|
411 ThreadProfile* Profile() const { return mProfile; } |
|
412 |
|
413 PlatformData* GetPlatformData() const { return mPlatformData; } |
|
414 void* StackTop() const { return mStackTop; } |
|
415 private: |
|
416 char* mName; |
|
417 int mThreadId; |
|
418 const bool mIsMainThread; |
|
419 PseudoStack* mPseudoStack; |
|
420 PlatformData* mPlatformData; |
|
421 ThreadProfile* mProfile; |
|
422 void* const mStackTop; |
|
423 }; |
|
424 |
|
425 #endif /* ndef TOOLS_PLATFORM_H_ */ |