Bug 1159486 - Make profiler use the process creation time. r=shu, r=jsantell
authorTom Tromey <tromey@mozilla.com>
Tue, 16 Jun 2015 22:28:00 -0400
changeset 249359 4583dd29ef0eb1c6d90057045c503352ae4dda32
parent 249358 1da97e961c3d2c9674b4fb621ab7f6b1efe48ee3
child 249360 f5fbae5bbe34a924d3a082efe62edf4503c94fb6
push id28923
push userryanvm@gmail.com
push dateWed, 17 Jun 2015 18:57:11 +0000
treeherdermozilla-central@099d6cd6725e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu, jsantell
bugs1159486
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1159486 - Make profiler use the process creation time. r=shu, r=jsantell
browser/devtools/performance/modules/logic/actors.js
browser/devtools/performance/modules/logic/recording-model.js
browser/devtools/performance/test/browser_perf-compatibility-05.js
browser/devtools/performance/test/browser_perf-data-massaging-01.js
toolkit/devtools/server/actors/profiler.js
tools/profiler/GeckoProfiler.h
tools/profiler/GeckoProfilerFunc.h
tools/profiler/GeckoProfilerImpl.h
tools/profiler/ProfileEntry.cpp
tools/profiler/ProfileEntry.h
tools/profiler/ProfileGatherer.cpp
tools/profiler/ProfileGatherer.h
tools/profiler/PseudoStack.h
tools/profiler/TableTicker.cpp
tools/profiler/TableTicker.h
tools/profiler/nsIProfiler.idl
tools/profiler/nsProfiler.cpp
tools/profiler/platform.cpp
tools/profiler/tests/gtest/ThreadProfileTest.cpp
widget/android/AndroidJNI.cpp
--- a/browser/devtools/performance/modules/logic/actors.js
+++ b/browser/devtools/performance/modules/logic/actors.js
@@ -114,20 +114,24 @@ ProfilerFrontFacade.prototype = {
 
     // Translate options from the recording model into profiler-specific
     // options for the nsIProfiler
     let profilerOptions = {
       entries: options.bufferSize,
       interval: options.sampleFrequency ? (1000 / (options.sampleFrequency * 1000)) : void 0
     };
 
-    yield this.startProfiler(profilerOptions);
+    let startInfo = yield this.startProfiler(profilerOptions);
+    let startTime = 0;
+    if ('currentTime' in startInfo) {
+      startTime = startInfo.currentTime;
+    }
 
     this.emit("profiler-activated");
-    return { startTime: 0, position, generation, totalSize };
+    return { startTime, position, generation, totalSize };
   }),
 
   /**
    * Indicates the end of a recording -- does not actually stop the profiler
    * (stopProfiler does that), but notes that we no longer need to poll
    * for buffer status.
    */
   stop: Task.async(function *() {
--- a/browser/devtools/performance/modules/logic/recording-model.js
+++ b/browser/devtools/performance/modules/logic/recording-model.js
@@ -139,17 +139,16 @@ RecordingModel.prototype = {
     this._duration = profilerEndTime - this._profilerStartTime;
     this._profile = profile;
     this._completed = true;
 
     // We filter out all samples that fall out of current profile's range
     // since the profiler is continuously running. Because of this, sample
     // times are not guaranteed to have a zero epoch, so offset the
     // timestamps.
-    // TODO move this into FakeProfilerFront in ./actors.js after bug 1154115
     RecordingUtils.offsetSampleTimes(this._profile, this._profilerStartTime);
 
     // Markers need to be sorted ascending by time, to be properly displayed
     // in a waterfall view.
     this._markers = this._markers.sort((a, b) => (a.start > b.start));
   }),
 
   /**
--- a/browser/devtools/performance/test/browser_perf-compatibility-05.js
+++ b/browser/devtools/performance/test/browser_perf-compatibility-05.js
@@ -32,18 +32,16 @@ function* spawnTest() {
   let firstRecordingStartTime = firstRecording._profilerStartTime;
   info("Started profiling at: " + firstRecordingStartTime);
 
   busyWait(WAIT_TIME); // allow the profiler module to sample some cpu activity
 
   yield front.stopRecording(firstRecording);
   info("The first recording is " + firstRecording.getDuration() + "ms long.");
 
-  is(firstRecordingStartTime, 0,
-    "The profiling start time should be 0 for the first recording.");
   ok(firstRecording.getDuration() >= WAIT_TIME,
     "The first recording duration is correct.");
 
   // Perform the second recording...
 
   let secondRecording = yield front.startRecording();
   let secondRecordingStartTime = secondRecording._profilerStartTime;
   info("Started profiling at: " + secondRecordingStartTime);
--- a/browser/devtools/performance/test/browser_perf-data-massaging-01.js
+++ b/browser/devtools/performance/test/browser_perf-data-massaging-01.js
@@ -17,18 +17,16 @@ function* spawnTest() {
   let firstRecording = yield front.startRecording();
   let firstRecordingStartTime = firstRecording._profilerStartTime;
   info("Started profiling at: " + firstRecordingStartTime);
 
   busyWait(WAIT_TIME); // allow the profiler module to sample some cpu activity
 
   yield front.stopRecording(firstRecording);
 
-  is(firstRecordingStartTime, 0,
-    "The profiling start time should be 0 for the first recording.");
   ok(firstRecording.getDuration() >= WAIT_TIME,
     "The first recording duration is correct.");
 
   // Perform the second recording...
 
   let secondRecording = yield front.startRecording();
   let secondRecordingStartTime = secondRecording._profilerStartTime;
   info("Started profiling at: " + secondRecordingStartTime);
--- a/toolkit/devtools/server/actors/profiler.js
+++ b/toolkit/devtools/server/actors/profiler.js
@@ -94,27 +94,31 @@ ProfilerActor.prototype = {
   onStartProfiler: function(request = {}) {
     let options = this._profilerStartOptions = {
       entries: request.entries || DEFAULT_PROFILER_OPTIONS.entries,
       interval: request.interval || DEFAULT_PROFILER_OPTIONS.interval,
       features: request.features || DEFAULT_PROFILER_OPTIONS.features,
       threadFilters: request.threadFilters || DEFAULT_PROFILER_OPTIONS.threadFilters,
     };
 
+    // The start time should be before any samples we might be
+    // interested in.
+    let currentTime = nsIProfilerModule.getElapsedTime();
+
     nsIProfilerModule.StartProfiler(
       options.entries,
       options.interval,
       options.features,
       options.features.length,
       options.threadFilters,
       options.threadFilters.length
     );
     let { position, totalSize, generation } = this.onGetBufferInfo();
 
-    return { started: true, position, totalSize, generation };
+    return { started: true, position, totalSize, generation, currentTime };
   },
 
   /**
    * Stops the nsIProfiler module, if no other client is using it.
    */
   onStopProfiler: function() {
     // Actually stop the profiler only if the last client has stopped profiling.
     // Since this is a root actor, and the profiler module interacts with the
--- a/tools/profiler/GeckoProfiler.h
+++ b/tools/profiler/GeckoProfiler.h
@@ -160,28 +160,28 @@ static inline bool profiler_feature_acti
 
 // Internal-only. Used by the event tracer.
 static inline void profiler_responsiveness(const mozilla::TimeStamp& aTime) {}
 
 // Internal-only.
 static inline void profiler_set_frame_number(int frameNumber) {}
 
 // Get the profile encoded as a JSON string.
-static inline mozilla::UniquePtr<char[]> profiler_get_profile(float aSinceTime = 0) {
+static inline mozilla::UniquePtr<char[]> profiler_get_profile(double aSinceTime = 0) {
   return nullptr;
 }
 
 // Get the profile encoded as a JSON object.
 static inline JSObject* profiler_get_profile_jsobject(JSContext* aCx,
-                                                      float aSinceTime = 0) {
+                                                      double aSinceTime = 0) {
   return nullptr;
 }
 
 // Get the profile encoded as a JSON object.
-static inline void profiler_get_profile_jsobject_async(float aSinceTime = 0,
+static inline void profiler_get_profile_jsobject_async(double aSinceTime = 0,
                                                        mozilla::dom::Promise* = 0) {}
 
 // Get the profile and write it into a file
 static inline void profiler_save_profile_to_file(char* aFilename) { }
 
 // Get the features supported by the profiler that are accepted by profiler_init.
 // Returns a null terminated char* array.
 static inline char** profiler_get_features() { return nullptr; }
--- a/tools/profiler/GeckoProfilerFunc.h
+++ b/tools/profiler/GeckoProfilerFunc.h
@@ -54,20 +54,20 @@ bool mozilla_sampler_feature_active(cons
 void mozilla_sampler_responsiveness(const mozilla::TimeStamp& time);
 
 void mozilla_sampler_frame_number(int frameNumber);
 
 const double* mozilla_sampler_get_responsiveness();
 
 void mozilla_sampler_save();
 
-mozilla::UniquePtr<char[]> mozilla_sampler_get_profile(float aSinceTime);
+mozilla::UniquePtr<char[]> mozilla_sampler_get_profile(double aSinceTime);
 
-JSObject *mozilla_sampler_get_profile_data(JSContext *aCx, float aSinceTime);
-void mozilla_sampler_get_profile_data_async(float aSinceTime,
+JSObject *mozilla_sampler_get_profile_data(JSContext* aCx, double aSinceTime);
+void mozilla_sampler_get_profile_data_async(double aSinceTime,
                                             mozilla::dom::Promise* aPromise);
 
 // Make this function easily callable from a debugger in a build without
 // debugging information (work around http://llvm.org/bugs/show_bug.cgi?id=22211)
 extern "C" {
   void mozilla_sampler_save_profile_to_file(const char* aFilename);
 }
 
--- a/tools/profiler/GeckoProfilerImpl.h
+++ b/tools/profiler/GeckoProfilerImpl.h
@@ -139,29 +139,29 @@ void profiler_responsiveness(const mozil
 
 static inline
 void profiler_set_frame_number(int frameNumber)
 {
   return mozilla_sampler_frame_number(frameNumber);
 }
 
 static inline
-mozilla::UniquePtr<char[]> profiler_get_profile(float aSinceTime = 0)
+mozilla::UniquePtr<char[]> profiler_get_profile(double aSinceTime = 0)
 {
   return mozilla_sampler_get_profile(aSinceTime);
 }
 
 static inline
-JSObject* profiler_get_profile_jsobject(JSContext* aCx, float aSinceTime = 0)
+JSObject* profiler_get_profile_jsobject(JSContext* aCx, double aSinceTime = 0)
 {
   return mozilla_sampler_get_profile_data(aCx, aSinceTime);
 }
 
 static inline
-void profiler_get_profile_jsobject_async(float aSinceTime = 0,
+void profiler_get_profile_jsobject_async(double aSinceTime = 0,
                                          mozilla::dom::Promise* aPromise = 0)
 {
   mozilla_sampler_get_profile_data_async(aSinceTime, aPromise);
 }
 
 static inline
 void profiler_save_profile_to_file(const char* aFilename)
 {
--- a/tools/profiler/ProfileEntry.cpp
+++ b/tools/profiler/ProfileEntry.cpp
@@ -48,18 +48,18 @@ ProfileEntry::ProfileEntry(char aTagName
   , mTagName(aTagName)
 { }
 
 ProfileEntry::ProfileEntry(char aTagName, void *aTagPtr)
   : mTagPtr(aTagPtr)
   , mTagName(aTagName)
 { }
 
-ProfileEntry::ProfileEntry(char aTagName, float aTagFloat)
-  : mTagFloat(aTagFloat)
+ProfileEntry::ProfileEntry(char aTagName, double aTagDouble)
+  : mTagDouble(aTagDouble)
   , mTagName(aTagName)
 { }
 
 ProfileEntry::ProfileEntry(char aTagName, uintptr_t aTagOffset)
   : mTagOffset(aTagOffset)
   , mTagName(aTagName)
 { }
 
@@ -558,22 +558,22 @@ void UniqueStacks::StreamFrame(const OnS
     }
   }
   mFrameTableWriter.EndArray();
 }
 
 struct ProfileSample
 {
   uint32_t mStack;
-  Maybe<float> mTime;
-  Maybe<float> mResponsiveness;
-  Maybe<float> mRSS;
-  Maybe<float> mUSS;
+  Maybe<double> mTime;
+  Maybe<double> mResponsiveness;
+  Maybe<double> mRSS;
+  Maybe<double> mUSS;
   Maybe<int> mFrameNumber;
-  Maybe<float> mPower;
+  Maybe<double> mPower;
 };
 
 static void WriteSample(SpliceableJSONWriter& aWriter, ProfileSample& aSample)
 {
   // Schema:
   //   [stack, time, responsiveness, rss, uss, frameNumber, power]
 
   aWriter.StartArrayElement();
@@ -627,58 +627,58 @@ static void WriteSample(SpliceableJSONWr
       aWriter.DoubleElement(*aSample.mPower);
     }
     index++;
   }
   aWriter.EndArray();
 }
 
 void ProfileBuffer::StreamSamplesToJSON(SpliceableJSONWriter& aWriter, int aThreadId,
-                                        float aSinceTime, JSRuntime* aRuntime,
+                                        double aSinceTime, JSRuntime* aRuntime,
                                         UniqueStacks& aUniqueStacks)
 {
   Maybe<ProfileSample> sample;
   int readPos = mReadPos;
   int currentThreadID = -1;
-  Maybe<float> currentTime;
+  Maybe<double> currentTime;
   UniquePtr<char[]> tagBuff = MakeUnique<char[]>(DYNAMIC_MAX_STRING);
 
   while (readPos != mWritePos) {
     ProfileEntry entry = mEntries[readPos];
     if (entry.mTagName == 'T') {
       currentThreadID = entry.mTagInt;
       currentTime.reset();
       int readAheadPos = (readPos + 1) % mEntrySize;
       if (readAheadPos != mWritePos) {
         ProfileEntry readAheadEntry = mEntries[readAheadPos];
         if (readAheadEntry.mTagName == 't') {
-          currentTime = Some(readAheadEntry.mTagFloat);
+          currentTime = Some(readAheadEntry.mTagDouble);
         }
       }
     }
     if (currentThreadID == aThreadId && (currentTime.isNothing() || *currentTime >= aSinceTime)) {
       switch (entry.mTagName) {
       case 'r':
         if (sample.isSome()) {
-          sample->mResponsiveness = Some(entry.mTagFloat);
+          sample->mResponsiveness = Some(entry.mTagDouble);
         }
         break;
       case 'p':
         if (sample.isSome()) {
-          sample->mPower = Some(entry.mTagFloat);
+          sample->mPower = Some(entry.mTagDouble);
         }
         break;
       case 'R':
         if (sample.isSome()) {
-          sample->mRSS = Some(entry.mTagFloat);
+          sample->mRSS = Some(entry.mTagDouble);
         }
         break;
       case 'U':
         if (sample.isSome()) {
-          sample->mUSS = Some(entry.mTagFloat);
+          sample->mUSS = Some(entry.mTagDouble);
          }
         break;
       case 'f':
         if (sample.isSome()) {
           sample->mFrameNumber = Some(entry.mTagInt);
         }
         break;
       case 's':
@@ -769,17 +769,17 @@ void ProfileBuffer::StreamSamplesToJSON(
     readPos = (readPos + 1) % mEntrySize;
   }
   if (sample.isSome()) {
     WriteSample(aWriter, *sample);
   }
 }
 
 void ProfileBuffer::StreamMarkersToJSON(SpliceableJSONWriter& aWriter, int aThreadId,
-                                        float aSinceTime, UniqueStacks& aUniqueStacks)
+                                        double aSinceTime, UniqueStacks& aUniqueStacks)
 {
   int readPos = mReadPos;
   int currentThreadID = -1;
   while (readPos != mWritePos) {
     ProfileEntry entry = mEntries[readPos];
     if (entry.mTagName == 'T') {
       currentThreadID = entry.mTagInt;
     } else if (currentThreadID == aThreadId && entry.mTagName == 'm') {
@@ -824,17 +824,17 @@ void ProfileBuffer::DuplicateLastSample(
        readPos != mWritePos;
        readPos = (readPos + 1) % mEntrySize) {
     switch (mEntries[readPos].mTagName) {
       case 'T':
         // We're done.
         return;
       case 't':
         // Copy with new time
-        addTag(ProfileEntry('t', static_cast<float>((mozilla::TimeStamp::Now() - sStartTime).ToMilliseconds())));
+        addTag(ProfileEntry('t', (mozilla::TimeStamp::Now() - sStartTime).ToMilliseconds()));
         break;
       case 'm':
         // Don't copy markers
         break;
       // Copy anything else we don't know about
       // L, B, S, c, s, d, l, f, h, r, t, p
       default:
         addTag(mEntries[readPos]);
@@ -882,17 +882,17 @@ void ThreadProfile::addTag(const Profile
 {
   mBuffer->addTag(aTag);
 }
 
 void ThreadProfile::addStoredMarker(ProfilerMarker *aStoredMarker) {
   mBuffer->addStoredMarker(aStoredMarker);
 }
 
-void ThreadProfile::StreamJSON(SpliceableJSONWriter& aWriter, float aSinceTime)
+void ThreadProfile::StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime)
 {
   // mUniqueStacks may already be emplaced from FlushSamplesAndMarkers.
   if (!mUniqueStacks.isSome()) {
     mUniqueStacks.emplace(mPseudoStack->mRuntime);
   }
 
   aWriter.Start(SpliceableJSONWriter::SingleLineStyle);
   {
@@ -939,17 +939,17 @@ void ThreadProfile::StreamJSON(Spliceabl
     }
     aWriter.EndArray();
   }
   aWriter.End();
 
   mUniqueStacks.reset();
 }
 
-void ThreadProfile::StreamSamplesAndMarkers(SpliceableJSONWriter& aWriter, float aSinceTime,
+void ThreadProfile::StreamSamplesAndMarkers(SpliceableJSONWriter& aWriter, double aSinceTime,
                                             UniqueStacks& aUniqueStacks)
 {
   // Thread meta data
   if (XRE_GetProcessType() == GeckoProcessType_Plugin) {
     // TODO Add the proper plugin name
     aWriter.StringProperty("name", "Plugin");
   } else if (XRE_GetProcessType() == GeckoProcessType_Content) {
     // This isn't going to really help once we have multiple content
--- a/tools/profiler/ProfileEntry.h
+++ b/tools/profiler/ProfileEntry.h
@@ -34,17 +34,17 @@ class ProfileEntry
 {
 public:
   ProfileEntry();
 
   // aTagData must not need release (i.e. be a string from the text segment)
   ProfileEntry(char aTagName, const char *aTagData);
   ProfileEntry(char aTagName, void *aTagPtr);
   ProfileEntry(char aTagName, ProfilerMarker *aTagMarker);
-  ProfileEntry(char aTagName, float aTagFloat);
+  ProfileEntry(char aTagName, double aTagDouble);
   ProfileEntry(char aTagName, uintptr_t aTagOffset);
   ProfileEntry(char aTagName, Address aTagAddress);
   ProfileEntry(char aTagName, int aTagLine);
   ProfileEntry(char aTagName, char aTagChar);
   bool is_ent_hint(char hintChar);
   bool is_ent_hint();
   bool is_ent(char tagName);
   void* get_tagPtr();
@@ -62,17 +62,17 @@ private:
   FRIEND_TEST(ThreadProfile, InsertTagsWrap);
   FRIEND_TEST(ThreadProfile, MemoryMeasure);
   friend class ProfileBuffer;
   union {
     const char* mTagData;
     char        mTagChars[sizeof(void*)];
     void*       mTagPtr;
     ProfilerMarker* mTagMarker;
-    float       mTagFloat;
+    double      mTagDouble;
     Address     mTagAddress;
     uintptr_t   mTagOffset;
     int         mTagInt;
     char        mTagChar;
   };
   char mTagName;
 };
 
@@ -224,19 +224,19 @@ private:
 
 class ProfileBuffer {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ProfileBuffer)
 
   explicit ProfileBuffer(int aEntrySize);
 
   void addTag(const ProfileEntry& aTag);
-  void StreamSamplesToJSON(SpliceableJSONWriter& aWriter, int aThreadId, float aSinceTime,
+  void StreamSamplesToJSON(SpliceableJSONWriter& aWriter, int aThreadId, double aSinceTime,
                            JSRuntime* rt, UniqueStacks& aUniqueStacks);
-  void StreamMarkersToJSON(SpliceableJSONWriter& aWriter, int aThreadId, float aSinceTime,
+  void StreamMarkersToJSON(SpliceableJSONWriter& aWriter, int aThreadId, double aSinceTime,
                            UniqueStacks& aUniqueStacks);
   void DuplicateLastSample(int aThreadId);
 
   void addStoredMarker(ProfilerMarker* aStoredMarker);
 
   // The following two methods are not signal safe! They delete markers.
   void deleteExpiredStoredMarkers();
   void reset();
@@ -371,17 +371,17 @@ public:
   /**
    * Track a marker which has been inserted into the ThreadProfile.
    * This marker can safely be deleted once the generation has
    * expired.
    */
   void addStoredMarker(ProfilerMarker *aStoredMarker);
   PseudoStack* GetPseudoStack();
   mozilla::Mutex* GetMutex();
-  void StreamJSON(SpliceableJSONWriter& aWriter, float aSinceTime = 0);
+  void StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime = 0);
 
   /**
    * Call this method when the JS entries inside the buffer are about to
    * become invalid, i.e., just before JS shutdown.
    */
   void FlushSamplesAndMarkers();
 
   void BeginUnwind();
@@ -404,17 +404,17 @@ public:
     mPlatformData = nullptr;
   }
 
   uint32_t bufferGeneration() const {
     return mBuffer->mGeneration;
   }
 
 protected:
-  void StreamSamplesAndMarkers(SpliceableJSONWriter& aWriter, float aSinceTime,
+  void StreamSamplesAndMarkers(SpliceableJSONWriter& aWriter, double aSinceTime,
                                UniqueStacks& aUniqueStacks);
 
 private:
   FRIEND_TEST(ThreadProfile, InsertOneTag);
   FRIEND_TEST(ThreadProfile, InsertOneTagWithTinyBuffer);
   FRIEND_TEST(ThreadProfile, InsertTagsNoWrap);
   FRIEND_TEST(ThreadProfile, InsertTagsWrap);
   FRIEND_TEST(ThreadProfile, MemoryMeasure);
--- a/tools/profiler/ProfileGatherer.cpp
+++ b/tools/profiler/ProfileGatherer.cpp
@@ -10,17 +10,17 @@
 using mozilla::dom::AutoJSAPI;
 using mozilla::dom::Promise;
 
 namespace mozilla {
 
 NS_IMPL_ISUPPORTS0(ProfileGatherer)
 
 ProfileGatherer::ProfileGatherer(TableTicker* aTicker,
-                                 float aSinceTime,
+                                 double aSinceTime,
                                  Promise* aPromise)
   : mPromise(aPromise)
   , mTicker(aTicker)
   , mSinceTime(aSinceTime)
   , mPendingProfiles(0)
 {
 }
 
--- a/tools/profiler/ProfileGatherer.h
+++ b/tools/profiler/ProfileGatherer.h
@@ -12,27 +12,27 @@ class TableTicker;
 namespace mozilla {
 
 class ProfileGatherer final : public nsISupports
 {
 public:
   NS_DECL_ISUPPORTS
 
   ProfileGatherer(TableTicker* aTicker,
-                  float aSinceTime,
+                  double aSinceTime,
                   mozilla::dom::Promise* aPromise);
   void WillGatherOOPProfile();
   void GatheredOOPProfile();
   void Start();
 
 private:
   ~ProfileGatherer() {};
   void Finish();
 
   nsRefPtr<mozilla::dom::Promise> mPromise;
   TableTicker* mTicker;
-  float mSinceTime;
+  double mSinceTime;
   uint32_t mPendingProfiles;
 };
 
 } // namespace mozilla
 
-#endif
\ No newline at end of file
+#endif
--- a/tools/profiler/PseudoStack.h
+++ b/tools/profiler/PseudoStack.h
@@ -77,39 +77,39 @@ class ProfilerLinkedList;
 class SpliceableJSONWriter;
 class UniqueStacks;
 
 class ProfilerMarker {
   friend class ProfilerLinkedList<ProfilerMarker>;
 public:
   explicit ProfilerMarker(const char* aMarkerName,
                           ProfilerMarkerPayload* aPayload = nullptr,
-                          float aTime = 0);
+                          double aTime = 0);
 
   ~ProfilerMarker();
 
   const char* GetMarkerName() const {
     return mMarkerName;
   }
 
   void StreamJSON(SpliceableJSONWriter& aWriter, UniqueStacks& aUniqueStacks) const;
 
   void SetGeneration(uint32_t aGenID);
 
   bool HasExpired(uint32_t aGenID) const {
     return mGenID + 2 <= aGenID;
   }
 
-  float GetTime() const;
+  double GetTime() const;
 
 private:
   char* mMarkerName;
   ProfilerMarkerPayload* mPayload;
   ProfilerMarker* mNext;
-  float mTime;
+  double mTime;
   uint32_t mGenID;
 };
 
 template<typename T>
 class ProfilerLinkedList {
 public:
   ProfilerLinkedList()
     : mHead(nullptr)
@@ -230,17 +230,17 @@ public:
 
   // This is called on every profiler restart. Put things that should happen at that time here.
   void reinitializeOnResume() {
     // This is needed to cause an initial sample to be taken from sleeping threads. Otherwise sleeping
     // threads would not have any samples to copy forward while sleeping.
     mSleepId++;
   }
 
-  void addMarker(const char *aMarkerStr, ProfilerMarkerPayload *aPayload, float aTime)
+  void addMarker(const char* aMarkerStr, ProfilerMarkerPayload* aPayload, double aTime)
   {
     ProfilerMarker* marker = new ProfilerMarker(aMarkerStr, aPayload, aTime);
     mPendingMarkers.insert(marker);
   }
 
   // called within signal. Function must be reentrant
   ProfilerMarkerLinkedList* getPendingMarkers()
   {
--- a/tools/profiler/TableTicker.cpp
+++ b/tools/profiler/TableTicker.cpp
@@ -154,17 +154,18 @@ TableTicker::TableTicker(double aInterva
 #endif
 
   // Deep copy aThreadNameFilters
   MOZ_ALWAYS_TRUE(mThreadNameFilters.resize(aFilterCount));
   for (uint32_t i = 0; i < aFilterCount; ++i) {
     mThreadNameFilters[i] = aThreadNameFilters[i];
   }
 
-  sStartTime = mozilla::TimeStamp::Now();
+  bool ignore;
+  sStartTime = mozilla::TimeStamp::ProcessCreation(ignore);
 
   {
     mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
 
     // Create ThreadProfile for each registered thread
     for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) {
       ThreadInfo* info = sRegisteredThreads->at(i);
 
@@ -308,23 +309,23 @@ void TableTicker::StreamMetaJSCustomObje
     nsAutoCString string;
 
     res = appInfo->GetName(string);
     if (!NS_FAILED(res))
       aWriter.StringProperty("product", string.Data());
   }
 }
 
-void TableTicker::ToStreamAsJSON(std::ostream& stream, float aSinceTime)
+void TableTicker::ToStreamAsJSON(std::ostream& stream, double aSinceTime)
 {
   SpliceableJSONWriter b(mozilla::MakeUnique<OStreamJSONWriteFunc>(stream));
   StreamJSON(b, aSinceTime);
 }
 
-JSObject* TableTicker::ToJSObject(JSContext *aCx, float aSinceTime)
+JSObject* TableTicker::ToJSObject(JSContext* aCx, double aSinceTime)
 {
   JS::RootedValue val(aCx);
   {
     UniquePtr<char[]> buf = ToJSON(aSinceTime);
     NS_ConvertUTF8toUTF16 js_string(nsDependentCString(buf.get()));
     bool rv = JS_ParseJSON(aCx, static_cast<const char16_t*>(js_string.get()),
                            js_string.Length(), &val);
     if (!rv) {
@@ -351,24 +352,24 @@ JSObject* TableTicker::ToJSObject(JSCont
         }
       }
 #endif
     }
   }
   return &val.toObject();
 }
 
-UniquePtr<char[]> TableTicker::ToJSON(float aSinceTime)
+UniquePtr<char[]> TableTicker::ToJSON(double aSinceTime)
 {
   SpliceableChunkedJSONWriter b;
   StreamJSON(b, aSinceTime);
   return b.WriteFunc()->CopyData();
 }
 
-void TableTicker::ToJSObjectAsync(float aSinceTime,
+void TableTicker::ToJSObjectAsync(double aSinceTime,
                                   Promise* aPromise)
 {
   if (NS_WARN_IF(mGatherer)) {
     return;
   }
 
   mGatherer = new ProfileGatherer(this, aSinceTime, aPromise);
   mGatherer->Start();
@@ -446,17 +447,17 @@ void BuildJavaThreadJSObject(SpliceableJ
       }
     }
 
   aWriter.EndArray();
   aWriter.End();
 }
 #endif
 
-void TableTicker::StreamJSON(SpliceableJSONWriter& aWriter, float aSinceTime)
+void TableTicker::StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime)
 {
   aWriter.Start(SpliceableJSONWriter::SingleLineStyle);
   {
     // Put shared library info
     aWriter.StringProperty("libs", GetSharedLibraryInfoString().c_str());
 
     // Put meta data
     aWriter.StartObjectProperty("meta");
@@ -1113,17 +1114,17 @@ void TableTicker::Tick(TickSample* sampl
 void TableTicker::InplaceTick(TickSample* sample)
 {
   ThreadProfile& currThreadProfile = *sample->threadProfile;
 
   currThreadProfile.addTag(ProfileEntry('T', currThreadProfile.ThreadId()));
 
   if (sample) {
     mozilla::TimeDuration delta = sample->timestamp - sStartTime;
-    currThreadProfile.addTag(ProfileEntry('t', static_cast<float>(delta.ToMilliseconds())));
+    currThreadProfile.addTag(ProfileEntry('t', delta.ToMilliseconds()));
   }
 
   PseudoStack* stack = currThreadProfile.GetPseudoStack();
 
 #if defined(USE_NS_STACKWALK) || defined(USE_EHABI_STACKWALK) || \
     defined(USE_LUL_STACKWALK)
   if (mUseStackWalk) {
     doNativeBacktrace(currThreadProfile, sample);
@@ -1142,33 +1143,33 @@ void TableTicker::InplaceTick(TickSample
       ProfilerMarker* marker = pendingMarkersList->popHead();
       currThreadProfile.addStoredMarker(marker);
       currThreadProfile.addTag(ProfileEntry('m', marker));
     }
   }
 
   if (sample && currThreadProfile.GetThreadResponsiveness()->HasData()) {
     mozilla::TimeDuration delta = currThreadProfile.GetThreadResponsiveness()->GetUnresponsiveDuration(sample->timestamp);
-    currThreadProfile.addTag(ProfileEntry('r', static_cast<float>(delta.ToMilliseconds())));
+    currThreadProfile.addTag(ProfileEntry('r', delta.ToMilliseconds()));
   }
 
   // rssMemory is equal to 0 when we are not recording.
   if (sample && sample->rssMemory != 0) {
-    currThreadProfile.addTag(ProfileEntry('R', static_cast<float>(sample->rssMemory)));
+    currThreadProfile.addTag(ProfileEntry('R', static_cast<double>(sample->rssMemory)));
   }
 
   // ussMemory is equal to 0 when we are not recording.
   if (sample && sample->ussMemory != 0) {
-    currThreadProfile.addTag(ProfileEntry('U', static_cast<float>(sample->ussMemory)));
+    currThreadProfile.addTag(ProfileEntry('U', static_cast<double>(sample->ussMemory)));
   }
 
 #if defined(XP_WIN)
   if (mProfilePower) {
     mIntelPowerGadget->TakeSample();
-    currThreadProfile.addTag(ProfileEntry('p', static_cast<float>(mIntelPowerGadget->GetTotalPackagePowerInWatts())));
+    currThreadProfile.addTag(ProfileEntry('p', static_cast<double>(mIntelPowerGadget->GetTotalPackagePowerInWatts())));
   }
 #endif
 
   if (sLastFrameNumber != sFrameNumber) {
     currThreadProfile.addTag(ProfileEntry('f', sFrameNumber));
     sLastFrameNumber = sFrameNumber;
   }
 }
--- a/tools/profiler/TableTicker.h
+++ b/tools/profiler/TableTicker.h
@@ -92,20 +92,20 @@ class TableTicker: public Sampler {
           break;
         }
       }
     }
 
     return mPrimaryThreadProfile;
   }
 
-  void ToStreamAsJSON(std::ostream& stream, float aSinceTime = 0);
-  virtual JSObject *ToJSObject(JSContext *aCx, float aSinceTime = 0);
-  mozilla::UniquePtr<char[]> ToJSON(float aSinceTime = 0);
-  virtual void ToJSObjectAsync(float aSinceTime = 0, mozilla::dom::Promise* aPromise = 0);
+  void ToStreamAsJSON(std::ostream& stream, double aSinceTime = 0);
+  virtual JSObject *ToJSObject(JSContext* aCx, double aSinceTime = 0);
+  mozilla::UniquePtr<char[]> ToJSON(double aSinceTime = 0);
+  virtual void ToJSObjectAsync(double aSinceTime = 0, mozilla::dom::Promise* aPromise = 0);
   void StreamMetaJSCustomObject(SpliceableJSONWriter& aWriter);
   void StreamTaskTracer(SpliceableJSONWriter& aWriter);
   void FlushOnJSShutdown(JSRuntime* aRuntime);
   bool ProfileJS() const { return mProfileJS; }
   bool ProfileJava() const { return mProfileJava; }
   bool ProfileGPU() const { return mProfileGPU; }
   bool ProfilePower() const { return mProfilePower; }
   bool ProfileThreads() const override { return mProfileThreads; }
@@ -123,17 +123,17 @@ class TableTicker: public Sampler {
 
 protected:
   // Called within a signal. This function must be reentrant
   virtual void InplaceTick(TickSample* sample);
 
   // Not implemented on platforms which do not support backtracing
   void doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample);
 
-  void StreamJSON(SpliceableJSONWriter& aWriter, float aSinceTime);
+  void StreamJSON(SpliceableJSONWriter& aWriter, double aSinceTime);
 
   // This represent the application's main thread (SAMPLER_INIT)
   ThreadProfile* mPrimaryThreadProfile;
   nsRefPtr<ProfileBuffer> mBuffer;
   bool mSaveRequested;
   bool mAddLeafAddresses;
   bool mUseStackWalk;
   bool mProfileJS;
--- a/tools/profiler/nsIProfiler.idl
+++ b/tools/profiler/nsIProfiler.idl
@@ -7,56 +7,57 @@
 
 %{C++
 template<class T> class nsTArray;
 class nsCString;
 %}
 
 [ref] native StringArrayRef(const nsTArray<nsCString>);
 
-[scriptable, uuid(0f474ec5-b95c-45d9-a7c8-156da0e3fa25)]
+[scriptable, uuid(921e1223-b1ea-4906-bb26-a846e6b6835b)]
 interface nsIProfiler : nsISupports
 {
   void StartProfiler(in uint32_t aEntries, in double aInterval,
                       [array, size_is(aFeatureCount)] in string aFeatures,
                       in uint32_t aFeatureCount,
                       [array, size_is(aFilterCount), optional] in string aThreadNameFilters,
                       [optional] in uint32_t aFilterCount);
   void StopProfiler();
   boolean IsPaused();
   void PauseSampling();
   void ResumeSampling();
   void AddMarker(in string aMarker);
   /*
    * Returns the JSON string of the profile. If aSinceTime is passed, only
    * report samples taken at >= aSinceTime.
    */
-  string GetProfile([optional] in float aSinceTime);
+  string GetProfile([optional] in double aSinceTime);
 
   /*
    * Returns a JS object of the profile. If aSinceTime is passed, only report
    * samples taken at >= aSinceTime.
    */
   [implicit_jscontext]
-  jsval getProfileData([optional] in float aSinceTime);
+  jsval getProfileData([optional] in double aSinceTime);
 
   [implicit_jscontext]
-  nsISupports getProfileDataAsync([optional] in float aSinceTime);
+  nsISupports getProfileDataAsync([optional] in double aSinceTime);
 
   boolean IsActive();
   void GetFeatures(out uint32_t aCount, [retval, array, size_is(aCount)] out string aFeatures);
 
   void GetBufferInfo(out uint32_t aCurrentPosition, out uint32_t aTotalSize,
                      out uint32_t aGeneration);
 
   /**
-   * Returns the elapsed time, in milliseconds, since the last StartProfiler call.
-   * Returns 0 if there is no active sampler.
+   * Returns the elapsed time, in milliseconds, since the profiler's epoch.
+   * The epoch is guaranteed to be constant for the duration of the
+   * process, but is otherwise arbitrary.
    */
-  float getElapsedTime();
+  double getElapsedTime();
 
   /**
    * Returns a JSON string of an array of shared library objects.
    * Every object has three properties: start, end, and name.
    * start and end are integers describing the address range that the library
    * occupies in memory. name is the path of the library as a string.
    *
    * On Windows profiling builds, the shared library objects will have
--- a/tools/profiler/nsProfiler.cpp
+++ b/tools/profiler/nsProfiler.cpp
@@ -116,17 +116,17 @@ nsProfiler::ResumeSampling()
 NS_IMETHODIMP
 nsProfiler::AddMarker(const char *aMarker)
 {
   PROFILER_MARKER(aMarker);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsProfiler::GetProfile(float aSinceTime, char **aProfile)
+nsProfiler::GetProfile(double aSinceTime, char** aProfile)
 {
   mozilla::UniquePtr<char[]> profile = profiler_get_profile(aSinceTime);
   if (profile) {
     size_t len = strlen(profile.get());
     char *profileStr = static_cast<char *>
                          (nsMemory::Clone(profile.get(), (len + 1) * sizeof(char)));
     profileStr[len] = '\0';
     *aProfile = profileStr;
@@ -200,29 +200,29 @@ nsProfiler::GetSharedLibraryInformation(
 NS_IMETHODIMP
 nsProfiler::DumpProfileToFile(const char* aFilename)
 {
   profiler_save_profile_to_file(aFilename);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsProfiler::GetProfileData(float aSinceTime, JSContext* aCx,
+nsProfiler::GetProfileData(double aSinceTime, JSContext* aCx,
                            JS::MutableHandle<JS::Value> aResult)
 {
   JS::RootedObject obj(aCx, profiler_get_profile_jsobject(aCx, aSinceTime));
   if (!obj) {
     return NS_ERROR_FAILURE;
   }
   aResult.setObject(*obj);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsProfiler::GetProfileDataAsync(float aSinceTime, JSContext* aCx,
+nsProfiler::GetProfileDataAsync(double aSinceTime, JSContext* aCx,
                                 nsISupports** aPromise)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (NS_WARN_IF(!aCx)) {
     return NS_ERROR_FAILURE;
   }
 
@@ -240,19 +240,19 @@ nsProfiler::GetProfileDataAsync(float aS
 
   profiler_get_profile_jsobject_async(aSinceTime, promise);
 
   promise.forget(aPromise);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsProfiler::GetElapsedTime(float* aElapsedTime)
+nsProfiler::GetElapsedTime(double* aElapsedTime)
 {
-  *aElapsedTime = static_cast<float>(profiler_time());
+  *aElapsedTime = profiler_time();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsProfiler::IsActive(bool *aIsActive)
 {
   *aIsActive = profiler_is_active();
   return NS_OK;
--- a/tools/profiler/platform.cpp
+++ b/tools/profiler/platform.cpp
@@ -9,16 +9,17 @@
 
 #include "ProfilerIOInterposeObserver.h"
 #include "platform.h"
 #include "PlatformMacros.h"
 #include "prenv.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/ThreadLocal.h"
+#include "mozilla/TimeStamp.h"
 #include "PseudoStack.h"
 #include "TableTicker.h"
 #include "nsIObserverService.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsProfilerStartParams.h"
 #include "mozilla/Services.h"
 #include "nsThreadUtils.h"
@@ -177,35 +178,35 @@ StackOwningThreadInfo::SetPendingDelete(
   PseudoStack* stack = Stack();
   if (stack) {
     stack->deref();
   }
   ThreadInfo::SetPendingDelete();
 }
 
 ProfilerMarker::ProfilerMarker(const char* aMarkerName,
-    ProfilerMarkerPayload* aPayload,
-    float aTime)
+                               ProfilerMarkerPayload* aPayload,
+                               double aTime)
   : mMarkerName(strdup(aMarkerName))
   , mPayload(aPayload)
   , mTime(aTime)
 {
 }
 
 ProfilerMarker::~ProfilerMarker() {
   free(mMarkerName);
   delete mPayload;
 }
 
 void
 ProfilerMarker::SetGeneration(uint32_t aGenID) {
   mGenID = aGenID;
 }
 
-float
+double
 ProfilerMarker::GetTime() const {
   return mTime;
 }
 
 void ProfilerMarker::StreamJSON(SpliceableJSONWriter& aWriter,
                                 UniqueStacks& aUniqueStacks) const
 {
   // Schema:
@@ -461,16 +462,19 @@ void mozilla_sampler_init(void* stackTop
   if (stack_key_initialized)
     return;
 
   LOG("BEGIN mozilla_sampler_init");
   if (!tlsPseudoStack.init() || !tlsTicker.init() || !tlsStackTop.init()) {
     LOG("Failed to init.");
     return;
   }
+  bool ignore;
+  sStartTime = mozilla::TimeStamp::ProcessCreation(ignore);
+
   stack_key_initialized = true;
 
   Sampler::Startup();
 
   PseudoStack *stack = PseudoStack::create();
   tlsPseudoStack.set(stack);
 
   bool isMainThread = true;
@@ -556,37 +560,37 @@ void mozilla_sampler_save()
   }
 
   t->RequestSave();
   // We're on the main thread already so we don't
   // have to wait to handle the save request.
   t->HandleSaveRequest();
 }
 
-mozilla::UniquePtr<char[]> mozilla_sampler_get_profile(float aSinceTime)
+mozilla::UniquePtr<char[]> mozilla_sampler_get_profile(double aSinceTime)
 {
   TableTicker *t = tlsTicker.get();
   if (!t) {
     return nullptr;
   }
 
   return t->ToJSON(aSinceTime);
 }
 
-JSObject *mozilla_sampler_get_profile_data(JSContext *aCx, float aSinceTime)
+JSObject* mozilla_sampler_get_profile_data(JSContext* aCx, double aSinceTime)
 {
   TableTicker *t = tlsTicker.get();
   if (!t) {
     return nullptr;
   }
 
   return t->ToJSObject(aCx, aSinceTime);
 }
 
-void mozilla_sampler_get_profile_data_async(float aSinceTime,
+void mozilla_sampler_get_profile_data_async(double aSinceTime,
                                             mozilla::dom::Promise* aPromise)
 {
   TableTicker *t = tlsTicker.get();
   if (NS_WARN_IF(!t)) {
     return;
   }
 
   t->ToJSObjectAsync(aSinceTime, aPromise);
@@ -963,19 +967,16 @@ void mozilla_sampler_sleep_end() {
     if (stack == nullptr) {
       return;
     }
     stack->setSleeping(0);
 }
 
 double mozilla_sampler_time(const mozilla::TimeStamp& aTime)
 {
-  if (!mozilla_sampler_is_active()) {
-    return 0.0;
-  }
   mozilla::TimeDuration delta = aTime - sStartTime;
   return delta.ToMilliseconds();
 }
 
 double mozilla_sampler_time()
 {
   return mozilla_sampler_time(mozilla::TimeStamp::Now());
 }
@@ -1044,15 +1045,15 @@ void mozilla_sampler_add_marker(const ch
   PseudoStack *stack = tlsPseudoStack.get();
   if (!stack) {
     return;
   }
 
   mozilla::TimeStamp origin = (aPayload && !aPayload->GetStartTime().IsNull()) ?
                      aPayload->GetStartTime() : mozilla::TimeStamp::Now();
   mozilla::TimeDuration delta = origin - sStartTime;
-  stack->addMarker(aMarker, payload.forget(), static_cast<float>(delta.ToMilliseconds()));
+  stack->addMarker(aMarker, payload.forget(), delta.ToMilliseconds());
 }
 
 // END externally visible functions
 ////////////////////////////////////////////////////////////////////////
 
 
--- a/tools/profiler/tests/gtest/ThreadProfileTest.cpp
+++ b/tools/profiler/tests/gtest/ThreadProfileTest.cpp
@@ -17,20 +17,20 @@ TEST(ThreadProfile, Initialization) {
 }
 
 // Make sure we can record one tag and read it
 TEST(ThreadProfile, InsertOneTag) {
   PseudoStack* stack = PseudoStack::create();
   Thread::tid_t tid = 1000;
   ThreadInfo info("testThread", tid, true, stack, nullptr);
   nsRefPtr<ProfileBuffer> pb = new ProfileBuffer(10);
-  pb->addTag(ProfileEntry('t', 123.1f));
+  pb->addTag(ProfileEntry('t', 123.1));
   ASSERT_TRUE(pb->mEntries != nullptr);
   ASSERT_TRUE(pb->mEntries[pb->mReadPos].mTagName == 't');
-  ASSERT_TRUE(pb->mEntries[pb->mReadPos].mTagFloat == 123.1f);
+  ASSERT_TRUE(pb->mEntries[pb->mReadPos].mTagDouble == 123.1);
 }
 
 // See if we can insert some tags
 TEST(ThreadProfile, InsertTagsNoWrap) {
   PseudoStack* stack = PseudoStack::create();
   Thread::tid_t tid = 1000;
   ThreadInfo info("testThread", tid, true, stack, nullptr);
   nsRefPtr<ProfileBuffer> pb = new ProfileBuffer(100);
--- a/widget/android/AndroidJNI.cpp
+++ b/widget/android/AndroidJNI.cpp
@@ -871,16 +871,19 @@ NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_dispatchMemoryPressure(JNIEnv* jenv, jclass)
 {
     NS_DispatchMemoryPressure(MemPressure_New);
 }
 
 NS_EXPORT jdouble JNICALL
 Java_org_mozilla_gecko_GeckoJavaSampler_getProfilerTime(JNIEnv *jenv, jclass jc)
 {
+  if (!profiler_is_active()) {
+    return 0.0;
+  }
   return profiler_time();
 }
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_gfx_NativePanZoomController_abortAnimation(JNIEnv* env, jobject instance)
 {
     APZCTreeManager *controller = nsWindow::GetAPZCTreeManager();
     if (controller) {