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 <string>
40 : #include <stdio.h>
41 : #include "sps_sampler.h"
42 : #include "platform.h"
43 : #include "nsXULAppAPI.h"
44 : #include "nsThreadUtils.h"
45 : #include "prenv.h"
46 : #include "shared-libraries.h"
47 : #include "mozilla/StringBuilder.h"
48 : #include "mozilla/StackWalk.h"
49 : #include "JSObjectBuilder.h"
50 :
51 : // we eventually want to make this runtime switchable
52 : #if defined(MOZ_PROFILING) && (defined(XP_UNIX) && !defined(XP_MACOSX))
53 : #ifndef ANDROID
54 : #define USE_BACKTRACE
55 : #endif
56 : #endif
57 : #ifdef USE_BACKTRACE
58 : #include <execinfo.h>
59 : #endif
60 :
61 : #if defined(MOZ_PROFILING) && (defined(XP_MACOSX) || defined(XP_WIN))
62 : #define USE_NS_STACKWALK
63 : #endif
64 : #ifdef USE_NS_STACKWALK
65 : #include "nsStackWalk.h"
66 : #endif
67 :
68 : using std::string;
69 : using namespace mozilla;
70 :
71 : #ifdef XP_WIN
72 : #include <windows.h>
73 : #define getpid GetCurrentProcessId
74 : #else
75 : #include <unistd.h>
76 : #endif
77 :
78 : #ifndef MAXPATHLEN
79 : #ifdef PATH_MAX
80 : #define MAXPATHLEN PATH_MAX
81 : #elif defined(MAX_PATH)
82 : #define MAXPATHLEN MAX_PATH
83 : #elif defined(_MAX_PATH)
84 : #define MAXPATHLEN _MAX_PATH
85 : #elif defined(CCHMAXPATH)
86 : #define MAXPATHLEN CCHMAXPATH
87 : #else
88 : #define MAXPATHLEN 1024
89 : #endif
90 : #endif
91 :
92 : #if _MSC_VER
93 : #define snprintf _snprintf
94 : #endif
95 :
96 :
97 : mozilla::tls::key pkey_stack;
98 : mozilla::tls::key pkey_ticker;
99 : // We need to track whether we've been initialized otherwise
100 : // we end up using pkey_stack without initializing it.
101 : // Because pkey_stack is totally opaque to us we can't reuse
102 : // it as the flag itself.
103 : bool stack_key_initialized;
104 :
105 1464 : TimeStamp sLastTracerEvent;
106 :
107 : class ThreadProfile;
108 :
109 : class ProfileEntry
110 : {
111 : public:
112 0 : ProfileEntry()
113 : : mTagData(NULL)
114 : , mLeafAddress(0)
115 0 : , mTagName(0)
116 0 : { }
117 :
118 : // aTagData must not need release (i.e. be a string from the text segment)
119 0 : ProfileEntry(char aTagName, const char *aTagData)
120 : : mTagData(aTagData)
121 : , mLeafAddress(0)
122 0 : , mTagName(aTagName)
123 0 : { }
124 :
125 : // aTagData must not need release (i.e. be a string from the text segment)
126 0 : ProfileEntry(char aTagName, const char *aTagData, Address aLeafAddress)
127 : : mTagData(aTagData)
128 : , mLeafAddress(aLeafAddress)
129 0 : , mTagName(aTagName)
130 0 : { }
131 :
132 0 : ProfileEntry(char aTagName, double aTagFloat)
133 : : mTagFloat(aTagFloat)
134 : , mLeafAddress(0)
135 0 : , mTagName(aTagName)
136 0 : { }
137 :
138 : ProfileEntry(char aTagName, uintptr_t aTagOffset)
139 : : mTagOffset(aTagOffset)
140 : , mLeafAddress(0)
141 : , mTagName(aTagName)
142 : { }
143 :
144 : string TagToString(ThreadProfile *profile);
145 :
146 : private:
147 : friend class ThreadProfile;
148 : union {
149 : const char* mTagData;
150 : double mTagFloat;
151 : Address mTagAddress;
152 : uintptr_t mTagOffset;
153 : };
154 : Address mLeafAddress;
155 : char mTagName;
156 : };
157 :
158 : #define PROFILE_MAX_ENTRY 100000
159 : class ThreadProfile
160 : {
161 : public:
162 0 : ThreadProfile(int aEntrySize, ProfileStack *aStack)
163 : : mWritePos(0)
164 : , mLastFlushPos(0)
165 : , mReadPos(0)
166 : , mEntrySize(aEntrySize)
167 0 : , mStack(aStack)
168 : {
169 0 : mEntries = new ProfileEntry[mEntrySize];
170 0 : }
171 :
172 0 : ~ThreadProfile()
173 : {
174 0 : delete[] mEntries;
175 0 : }
176 :
177 0 : void addTag(ProfileEntry aTag)
178 : {
179 : // Called from signal, call only reentrant functions
180 0 : mEntries[mWritePos] = aTag;
181 0 : mWritePos = (mWritePos + 1) % mEntrySize;
182 0 : if (mWritePos == mReadPos) {
183 : // Keep one slot open
184 0 : mEntries[mReadPos] = ProfileEntry();
185 0 : mReadPos = (mReadPos + 1) % mEntrySize;
186 : }
187 : // we also need to move the flush pos to ensure we
188 : // do not pass it
189 0 : if (mWritePos == mLastFlushPos) {
190 0 : mLastFlushPos = (mLastFlushPos + 1) % mEntrySize;
191 : }
192 0 : }
193 :
194 : // flush the new entries
195 0 : void flush()
196 : {
197 0 : mLastFlushPos = mWritePos;
198 0 : }
199 :
200 : // discards all of the entries since the last flush()
201 : // NOTE: that if mWritePos happens to wrap around past
202 : // mLastFlushPos we actually only discard mWritePos - mLastFlushPos entries
203 : //
204 : // r = mReadPos
205 : // w = mWritePos
206 : // f = mLastFlushPos
207 : //
208 : // r f w
209 : // |-----------------------------|
210 : // | abcdefghijklmnopq | -> 'abcdefghijklmnopq'
211 : // |-----------------------------|
212 : //
213 : //
214 : // mWritePos and mReadPos have passed mLastFlushPos
215 : // f
216 : // w r
217 : // |-----------------------------|
218 : // |ABCDEFGHIJKLMNOPQRSqrstuvwxyz|
219 : // |-----------------------------|
220 : // w
221 : // r
222 : // |-----------------------------|
223 : // |ABCDEFGHIJKLMNOPQRSqrstuvwxyz| -> ''
224 : // |-----------------------------|
225 : //
226 : //
227 : // mWritePos will end up the same as mReadPos
228 : // r
229 : // w f
230 : // |-----------------------------|
231 : // |ABCDEFGHIJKLMklmnopqrstuvwxyz|
232 : // |-----------------------------|
233 : // r
234 : // w
235 : // |-----------------------------|
236 : // |ABCDEFGHIJKLMklmnopqrstuvwxyz| -> ''
237 : // |-----------------------------|
238 : //
239 : //
240 : // mWritePos has moved past mReadPos
241 : // w r f
242 : // |-----------------------------|
243 : // |ABCDEFdefghijklmnopqrstuvwxyz|
244 : // |-----------------------------|
245 : // r w
246 : // |-----------------------------|
247 : // |ABCDEFdefghijklmnopqrstuvwxyz| -> 'defghijkl'
248 : // |-----------------------------|
249 :
250 0 : void erase()
251 : {
252 0 : mWritePos = mLastFlushPos;
253 0 : }
254 :
255 0 : void ToString(StringBuilder &profile)
256 : {
257 0 : int readPos = mReadPos;
258 0 : while (readPos != mLastFlushPos) {
259 0 : profile.Append(mEntries[readPos].TagToString(this).c_str());
260 0 : readPos = (readPos + 1) % mEntrySize;
261 : }
262 0 : }
263 :
264 0 : JSObject *ToJSObject(JSContext *aCx)
265 : {
266 0 : JSObjectBuilder b(aCx);
267 :
268 0 : JSObject *profile = b.CreateObject();
269 0 : JSObject *samples = b.CreateArray();
270 0 : b.DefineProperty(profile, "samples", samples);
271 :
272 0 : JSObject *sample = NULL;
273 0 : JSObject *frames = NULL;
274 :
275 0 : int oldReadPos = mReadPos;
276 0 : while (mReadPos != mLastFlushPos) {
277 0 : ProfileEntry entry = mEntries[mReadPos];
278 0 : mReadPos = (mReadPos + 1) % mEntrySize;
279 0 : switch (entry.mTagName) {
280 : case 's':
281 0 : sample = b.CreateObject();
282 0 : b.DefineProperty(sample, "name", (const char*)entry.mTagData);
283 0 : frames = b.CreateArray();
284 0 : b.DefineProperty(sample, "frames", frames);
285 0 : b.ArrayPush(samples, sample);
286 0 : break;
287 : case 'c':
288 : case 'l':
289 : {
290 0 : if (sample) {
291 0 : JSObject *frame = b.CreateObject();
292 : char tagBuff[1024];
293 0 : void* pc = (void*)entry.mTagData;
294 0 : snprintf(tagBuff, 1024, "%p", pc);
295 0 : b.DefineProperty(frame, "location", tagBuff);
296 0 : b.ArrayPush(frames, frame);
297 : }
298 : }
299 : }
300 : }
301 0 : mReadPos = oldReadPos;
302 :
303 0 : return profile;
304 : }
305 :
306 0 : void WriteProfile(FILE* stream)
307 : {
308 0 : int readPos = mReadPos;
309 0 : while (readPos != mLastFlushPos) {
310 0 : string tag = mEntries[readPos].TagToString(this);
311 0 : fwrite(tag.data(), 1, tag.length(), stream);
312 0 : readPos = (readPos + 1) % mEntrySize;
313 : }
314 0 : }
315 :
316 0 : ProfileStack* GetStack()
317 : {
318 0 : return mStack;
319 : }
320 : private:
321 : // Circular buffer 'Keep One Slot Open' implementation
322 : // for simplicity
323 : ProfileEntry *mEntries;
324 : int mWritePos; // points to the next entry we will write to
325 : int mLastFlushPos; // points to the next entry since the last flush()
326 : int mReadPos; // points to the next entry we will read to
327 : int mEntrySize;
328 : ProfileStack *mStack;
329 : };
330 :
331 : class SaveProfileTask;
332 :
333 : static bool
334 0 : hasFeature(const char** aFeatures, uint32_t aFeatureCount, const char* aFeature) {
335 0 : for(size_t i = 0; i < aFeatureCount; i++) {
336 0 : if (strcmp(aFeatures[i], aFeature) == 0)
337 0 : return true;
338 : }
339 0 : return false;
340 : }
341 :
342 : class TableTicker: public Sampler {
343 : public:
344 0 : TableTicker(int aInterval, int aEntrySize, ProfileStack *aStack,
345 : const char** aFeatures, uint32_t aFeatureCount)
346 : : Sampler(aInterval, true)
347 : , mPrimaryThreadProfile(aEntrySize, aStack)
348 0 : , mSaveRequested(false)
349 : {
350 0 : mUseStackWalk = hasFeature(aFeatures, aFeatureCount, "stackwalk");
351 : //XXX: It's probably worth splitting the jank profiler out from the regular profiler at some point
352 0 : mJankOnly = hasFeature(aFeatures, aFeatureCount, "jank");
353 0 : mPrimaryThreadProfile.addTag(ProfileEntry('m', "Start"));
354 0 : }
355 :
356 0 : ~TableTicker() { if (IsActive()) Stop(); }
357 :
358 0 : virtual void SampleStack(TickSample* sample) {}
359 :
360 : // Called within a signal. This function must be reentrant
361 : virtual void Tick(TickSample* sample);
362 :
363 : // Called within a signal. This function must be reentrant
364 0 : virtual void RequestSave()
365 : {
366 0 : mSaveRequested = true;
367 0 : }
368 :
369 : virtual void HandleSaveRequest();
370 :
371 0 : ThreadProfile* GetPrimaryThreadProfile()
372 : {
373 0 : return &mPrimaryThreadProfile;
374 : }
375 :
376 : JSObject *ToJSObject(JSContext *aCx);
377 :
378 : private:
379 : // Not implemented on platforms which do not support backtracing
380 : void doBacktrace(ThreadProfile &aProfile, Address pc);
381 :
382 : private:
383 : // This represent the application's main thread (SAMPLER_INIT)
384 : ThreadProfile mPrimaryThreadProfile;
385 : bool mSaveRequested;
386 : bool mUseStackWalk;
387 : bool mJankOnly;
388 : };
389 :
390 : /**
391 : * This is an event used to save the profile on the main thread
392 : * to be sure that it is not being modified while saving.
393 : */
394 0 : class SaveProfileTask : public nsRunnable {
395 : public:
396 0 : SaveProfileTask() {}
397 :
398 0 : NS_IMETHOD Run() {
399 0 : TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
400 :
401 : char buff[MAXPATHLEN];
402 : #ifdef ANDROID
403 : #define FOLDER "/sdcard/"
404 : #elif defined(XP_WIN)
405 : #define FOLDER "%TEMP%\\"
406 : #else
407 : #define FOLDER "/tmp/"
408 : #endif
409 :
410 0 : snprintf(buff, MAXPATHLEN, "%sprofile_%i_%i.txt", FOLDER, XRE_GetProcessType(), getpid());
411 :
412 : #ifdef XP_WIN
413 : // Expand %TEMP% on Windows
414 : {
415 : char tmp[MAXPATHLEN];
416 : ExpandEnvironmentStringsA(buff, tmp, mozilla::ArrayLength(tmp));
417 : strcpy(buff, tmp);
418 : }
419 : #endif
420 :
421 0 : FILE* stream = ::fopen(buff, "w");
422 0 : if (stream) {
423 0 : t->GetPrimaryThreadProfile()->WriteProfile(stream);
424 0 : ::fclose(stream);
425 0 : LOG("Saved to " FOLDER "profile_TYPE_PID.txt");
426 : } else {
427 0 : LOG("Fail to open profile log file.");
428 : }
429 :
430 0 : return NS_OK;
431 : }
432 : };
433 :
434 0 : void TableTicker::HandleSaveRequest()
435 : {
436 0 : if (!mSaveRequested)
437 0 : return;
438 0 : mSaveRequested = false;
439 :
440 : // TODO: Use use the ipc/chromium Tasks here to support processes
441 : // without XPCOM.
442 0 : nsCOMPtr<nsIRunnable> runnable = new SaveProfileTask();
443 0 : NS_DispatchToMainThread(runnable);
444 : }
445 :
446 0 : JSObject* TableTicker::ToJSObject(JSContext *aCx)
447 : {
448 0 : JSObjectBuilder b(aCx);
449 :
450 0 : JSObject *profile = b.CreateObject();
451 :
452 : // Put meta data
453 : // TODO: List things like feature, version number, profile time start/end
454 :
455 : // Lists the samples for each ThreadProfile
456 0 : JSObject *threads = b.CreateArray();
457 0 : b.DefineProperty(profile, "threads", threads);
458 :
459 : // For now we only have one thread
460 0 : JSObject* threadSamples = GetPrimaryThreadProfile()->ToJSObject(aCx);
461 0 : b.ArrayPush(threads, threadSamples);
462 :
463 0 : return profile;
464 : }
465 :
466 :
467 : #ifdef USE_BACKTRACE
468 : void TableTicker::doBacktrace(ThreadProfile &aProfile, Address pc)
469 : {
470 : void *array[100];
471 : int count = backtrace (array, 100);
472 :
473 : aProfile.addTag(ProfileEntry('s', "(root)", 0));
474 :
475 : for (int i = 0; i < count; i++) {
476 : if( (intptr_t)array[i] == -1 ) break;
477 : aProfile.addTag(ProfileEntry('l', (const char*)array[i]));
478 : }
479 : }
480 : #endif
481 :
482 :
483 : #ifdef USE_NS_STACKWALK
484 : typedef struct {
485 : void** array;
486 : size_t size;
487 : size_t count;
488 : } PCArray;
489 :
490 : static
491 : void StackWalkCallback(void* aPC, void* aClosure)
492 : {
493 : PCArray* array = static_cast<PCArray*>(aClosure);
494 : if (array->count >= array->size) {
495 : // too many frames, ignore
496 : return;
497 : }
498 : array->array[array->count++] = aPC;
499 : }
500 :
501 : void TableTicker::doBacktrace(ThreadProfile &aProfile, Address fp)
502 : {
503 : #ifndef XP_MACOSX
504 : uintptr_t thread = GetThreadHandle(platform_data());
505 : MOZ_ASSERT(thread);
506 : #endif
507 : void* pc_array[1000];
508 : PCArray array = {
509 : pc_array,
510 : mozilla::ArrayLength(pc_array),
511 : 0
512 : };
513 : #ifdef XP_MACOSX
514 : pthread_t pt = GetProfiledThread(platform_data());
515 : void *stackEnd = reinterpret_cast<void*>(-1);
516 : if (pt)
517 : stackEnd = static_cast<char*>(pthread_get_stackaddr_np(pt));
518 : nsresult rv = FramePointerStackWalk(StackWalkCallback, 1, &array, reinterpret_cast<void**>(fp), stackEnd);
519 : #else
520 : nsresult rv = NS_StackWalk(StackWalkCallback, 0, &array, thread);
521 : #endif
522 : if (NS_SUCCEEDED(rv)) {
523 : aProfile.addTag(ProfileEntry('s', "(root)", 0));
524 :
525 : for (size_t i = array.count; i > 0; --i) {
526 : aProfile.addTag(ProfileEntry('l', (const char*)array.array[i - 1]));
527 : }
528 : }
529 : }
530 : #endif
531 :
532 : static
533 0 : void doSampleStackTrace(ProfileStack *aStack, ThreadProfile &aProfile, TickSample *sample)
534 : {
535 : // Sample
536 : // 's' tag denotes the start of a sample block
537 : // followed by 0 or more 'c' tags.
538 0 : for (int i = 0; i < aStack->mStackPointer; i++) {
539 0 : if (i == 0) {
540 0 : Address pc = 0;
541 0 : if (sample) {
542 0 : pc = sample->pc;
543 : }
544 0 : aProfile.addTag(ProfileEntry('s', aStack->mStack[i], pc));
545 : } else {
546 0 : aProfile.addTag(ProfileEntry('c', aStack->mStack[i]));
547 : }
548 : }
549 0 : }
550 :
551 : /* used to keep track of the last event that we sampled during */
552 : unsigned int sLastSampledEventGeneration = 0;
553 :
554 : /* a counter that's incremented everytime we get responsiveness event
555 : * note: it might also be worth tracking everytime we go around
556 : * the event loop */
557 : unsigned int sCurrentEventGeneration = 0;
558 : /* we don't need to worry about overflow because we only treat the
559 : * case of them being the same as special. i.e. we only run into
560 : * a problem if 2^32 events happen between samples that we need
561 : * to know are associated with different events */
562 :
563 0 : void TableTicker::Tick(TickSample* sample)
564 : {
565 : // Marker(s) come before the sample
566 0 : ProfileStack* stack = mPrimaryThreadProfile.GetStack();
567 0 : for (int i = 0; stack->getMarker(i) != NULL; i++) {
568 0 : mPrimaryThreadProfile.addTag(ProfileEntry('m', stack->getMarker(i)));
569 : }
570 0 : stack->mQueueClearMarker = true;
571 :
572 0 : bool recordSample = true;
573 0 : if (mJankOnly) {
574 : // if we are on a different event we can discard any temporary samples
575 : // we've kept around
576 0 : if (sLastSampledEventGeneration != sCurrentEventGeneration) {
577 : // XXX: we also probably want to add an entry to the profile to help
578 : // distinguish which samples are part of the same event. That, or record
579 : // the event generation in each sample
580 0 : mPrimaryThreadProfile.erase();
581 : }
582 0 : sLastSampledEventGeneration = sCurrentEventGeneration;
583 :
584 0 : recordSample = false;
585 : // only record the events when we have a we haven't seen a tracer event for 100ms
586 0 : if (!sLastTracerEvent.IsNull()) {
587 0 : TimeDuration delta = sample->timestamp - sLastTracerEvent;
588 0 : if (delta.ToMilliseconds() > 100.0) {
589 0 : recordSample = true;
590 : }
591 : }
592 : }
593 :
594 : #if defined(USE_BACKTRACE) || defined(USE_NS_STACKWALK)
595 : if (mUseStackWalk) {
596 : doBacktrace(mPrimaryThreadProfile, sample->fp);
597 : } else {
598 : doSampleStackTrace(stack, mPrimaryThreadProfile, sample);
599 : }
600 : #else
601 0 : doSampleStackTrace(stack, mPrimaryThreadProfile, sample);
602 : #endif
603 :
604 0 : if (recordSample)
605 0 : mPrimaryThreadProfile.flush();
606 :
607 0 : if (!mJankOnly && !sLastTracerEvent.IsNull() && sample) {
608 0 : TimeDuration delta = sample->timestamp - sLastTracerEvent;
609 0 : mPrimaryThreadProfile.addTag(ProfileEntry('r', delta.ToMilliseconds()));
610 : }
611 0 : }
612 :
613 0 : string ProfileEntry::TagToString(ThreadProfile *profile)
614 : {
615 0 : string tag = "";
616 0 : if (mTagName == 'r') {
617 : char buff[50];
618 0 : snprintf(buff, 50, "%-40f", mTagFloat);
619 0 : tag += string(1, mTagName) + string("-") + string(buff) + string("\n");
620 0 : } else if (mTagName == 'l') {
621 : char tagBuff[1024];
622 0 : Address pc = mTagAddress;
623 0 : snprintf(tagBuff, 1024, "l-%p\n", pc);
624 0 : tag += string(tagBuff);
625 : } else {
626 0 : tag += string(1, mTagName) + string("-") + string(mTagData) + string("\n");
627 : }
628 :
629 : #ifdef ENABLE_SPS_LEAF_DATA
630 : if (mLeafAddress) {
631 : char tagBuff[1024];
632 : unsigned long pc = (unsigned long)mLeafAddress;
633 : snprintf(tagBuff, 1024, "l-%llu\n", pc);
634 : tag += string(tagBuff);
635 : }
636 : #endif
637 : return tag;
638 : }
639 :
640 : #define PROFILE_DEFAULT_ENTRY 100000
641 : #define PROFILE_DEFAULT_INTERVAL 10
642 : #define PROFILE_DEFAULT_FEATURES NULL
643 : #define PROFILE_DEFAULT_FEATURE_COUNT 0
644 :
645 23 : void mozilla_sampler_init()
646 : {
647 : // TODO linux port: Use TLS with ifdefs
648 46 : if (!mozilla::tls::create(&pkey_stack) ||
649 23 : !mozilla::tls::create(&pkey_ticker)) {
650 0 : LOG("Failed to init.");
651 0 : return;
652 : }
653 23 : stack_key_initialized = true;
654 :
655 23 : ProfileStack *stack = new ProfileStack();
656 23 : mozilla::tls::set(pkey_stack, stack);
657 :
658 : // We can't open pref so we use an environment variable
659 : // to know if we should trigger the profiler on startup
660 : // NOTE: Default
661 23 : const char *val = PR_GetEnv("MOZ_PROFILER_STARTUP");
662 23 : if (!val || !*val) {
663 23 : return;
664 : }
665 :
666 : mozilla_sampler_start(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL,
667 0 : PROFILE_DEFAULT_FEATURES, PROFILE_DEFAULT_FEATURE_COUNT);
668 : }
669 :
670 0 : void mozilla_sampler_deinit()
671 : {
672 0 : mozilla_sampler_stop();
673 : // We can't delete the Stack because we can be between a
674 : // sampler call_enter/call_exit point.
675 : // TODO Need to find a safe time to delete Stack
676 0 : }
677 :
678 0 : void mozilla_sampler_save()
679 : {
680 0 : TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
681 0 : if (!t) {
682 0 : return;
683 : }
684 :
685 0 : t->RequestSave();
686 : // We're on the main thread already so we don't
687 : // have to wait to handle the save request.
688 0 : t->HandleSaveRequest();
689 : }
690 :
691 0 : char* mozilla_sampler_get_profile()
692 : {
693 0 : TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
694 0 : if (!t) {
695 0 : return NULL;
696 : }
697 :
698 0 : StringBuilder profile;
699 0 : t->GetPrimaryThreadProfile()->ToString(profile);
700 :
701 0 : char *rtn = (char*)malloc( (profile.Length()+1) * sizeof(char) );
702 0 : strcpy(rtn, profile.Buffer());
703 0 : return rtn;
704 : }
705 :
706 0 : JSObject *mozilla_sampler_get_profile_data(JSContext *aCx)
707 : {
708 0 : TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
709 0 : if (!t) {
710 0 : return NULL;
711 : }
712 :
713 0 : return t->ToJSObject(aCx);
714 : }
715 :
716 :
717 0 : const char** mozilla_sampler_get_features()
718 : {
719 : static const char* features[] = {
720 : #if defined(MOZ_PROFILING) && (defined(USE_BACKTRACE) || defined(USE_NS_STACKWALK))
721 : "stackwalk",
722 : #endif
723 : "jank",
724 : NULL
725 : };
726 :
727 0 : return features;
728 : }
729 :
730 : // Values are only honored on the first start
731 0 : void mozilla_sampler_start(int aProfileEntries, int aInterval,
732 : const char** aFeatures, uint32_t aFeatureCount)
733 : {
734 0 : ProfileStack *stack = mozilla::tls::get<ProfileStack>(pkey_stack);
735 0 : if (!stack) {
736 0 : ASSERT(false);
737 0 : return;
738 : }
739 :
740 0 : mozilla_sampler_stop();
741 :
742 : TableTicker *t = new TableTicker(aInterval, aProfileEntries, stack,
743 0 : aFeatures, aFeatureCount);
744 0 : mozilla::tls::set(pkey_ticker, t);
745 0 : t->Start();
746 : }
747 :
748 0 : void mozilla_sampler_stop()
749 : {
750 0 : TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
751 0 : if (!t) {
752 0 : return;
753 : }
754 :
755 0 : t->Stop();
756 0 : mozilla::tls::set(pkey_ticker, (ProfileStack*)NULL);
757 : }
758 :
759 0 : bool mozilla_sampler_is_active()
760 : {
761 0 : TableTicker *t = mozilla::tls::get<TableTicker>(pkey_ticker);
762 0 : if (!t) {
763 0 : return false;
764 : }
765 :
766 0 : return t->IsActive();
767 : }
768 :
769 : double sResponsivenessTimes[100];
770 : double sCurrResponsiveness = 0.f;
771 : unsigned int sResponsivenessLoc = 0;
772 0 : void mozilla_sampler_responsiveness(TimeStamp aTime)
773 : {
774 0 : if (!sLastTracerEvent.IsNull()) {
775 0 : if (sResponsivenessLoc == 100) {
776 0 : for(size_t i = 0; i < 100-1; i++) {
777 0 : sResponsivenessTimes[i] = sResponsivenessTimes[i+1];
778 : }
779 0 : sResponsivenessLoc--;
780 : }
781 0 : TimeDuration delta = aTime - sLastTracerEvent;
782 0 : sResponsivenessTimes[sResponsivenessLoc++] = delta.ToMilliseconds();
783 : }
784 0 : sCurrentEventGeneration++;
785 :
786 0 : sLastTracerEvent = aTime;
787 0 : }
788 :
789 0 : const double* mozilla_sampler_get_responsiveness()
790 : {
791 0 : return sResponsivenessTimes;
792 4392 : }
793 :
|