Closed Bug 1429904 Opened 2 years ago Closed 2 years ago

ThreadInfo::FlushSamplesAndMarkers can cause loss of samples/markers for other threads

Categories

(Core :: Gecko Profiler, enhancement, P1)

enhancement

Tracking

()

RESOLVED FIXED
mozilla60
Tracking Status
firefox60 --- fixed

People

(Reporter: jchen, Assigned: mstange)

Details

Attachments

(15 files, 2 obsolete files)

59 bytes, text/x-review-board-request
njn
: review+
Details
59 bytes, text/x-review-board-request
njn
: review+
Details
59 bytes, text/x-review-board-request
njn
: review+
Details
59 bytes, text/x-review-board-request
njn
: review+
Details
59 bytes, text/x-review-board-request
njn
: review+
Details
59 bytes, text/x-review-board-request
njn
: review+
Details
59 bytes, text/x-review-board-request
njn
: review+
Details
59 bytes, text/x-review-board-request
njn
: review+
Details
59 bytes, text/x-review-board-request
njn
: review+
Details
59 bytes, text/x-review-board-request
njn
: review+
Details
59 bytes, text/x-review-board-request
njn
: review+
Details
59 bytes, text/x-review-board-request
njn
: review+
Details
59 bytes, text/x-review-board-request
njn
: review+
Details
59 bytes, text/x-review-board-request
njn
: review+
Details
59 bytes, text/x-review-board-request
njn
: review+
Details
`ThreadInfo::FlushSamplesAndMarkers()` is supposed to commit a particular thread's samples/markers to JSON, and then remove those samples/markers from the active buffer.

However, it calls `aBuffer.Reset()` at the end, which clears all samples/markers for all threads. The result is lost samples/markers for other threads that haven't been flushed yet.
How did you run into this?

This is likely a contributing factor to bug 1428076, which occurs when ThreadInfo::FlushSamplesAndMarkers() is called but there is no data to flush. If a previous thread has just flushed away the current thread's data, this is more likely to happen.
I was running a profile until shutdown (using MOZ_PROFILER_SHUTDOWN env var), and profiling multiple JS threads (GeckoMain + DOM worker). I think what ends up happening on shutdown is the DOM worker thread has its JS context destroyed first, causing the FlushSamplesAndMarkers() call (and the main thread markers to be deleted). The resulting profile is therefore missing markers for the main thread, except the shutdown markers, which occur after the DOM worker has shutdown.
I see. Great find!
I think that's what I see in https://perfht.ml/2mPbTeh (this is a custom build with patch for bug 1428076 with the STR from bug 1428076 comment 3): we can suppose one of the workers has been "flushed", and we see we miss all samples for all other threads. One worker got some samples though, I don't really know why.
(In reply to Julien Wajsberg [:julienw] from comment #4)
> One worker got some samples though, I don't
> really know why.

That's probably the first one that triggered a flush, and its data successfully made it into its mSavedStreamedSamples.
I meant, 1 worker beside the one that got unregistered.
Assignee: nobody → mstange
Status: NEW → ASSIGNED
Priority: -- → P1
Attachment #8951928 - Attachment is obsolete: true
Attachment #8951928 - Flags: review?(n.nethercote)
Comment on attachment 8951919 [details]
Bug 1429904 - Make FrameKey members const.

https://reviewboard.mozilla.org/r/221180/#review227090

This patch is making more changes than the commit message describes. Please expand the log message. It looks like it could also be split into two or three patches.
Attachment #8951919 - Flags: review?(n.nethercote) → review-
Comment on attachment 8951920 [details]
Bug 1429904 - Add 'using namespace mozilla;' to ProfileBufferEntry.cpp and remove some mozilla:: prefixes.

https://reviewboard.mozilla.org/r/221182/#review227092
Attachment #8951920 - Flags: review?(n.nethercote) → review+
Comment on attachment 8951921 [details]
Bug 1429904 - Use a Variant to split the FrameKey members into two groups.

https://reviewboard.mozilla.org/r/221184/#review227094

r=me with the unnecessary AddToHash() calls removed.

::: tools/profiler/core/ProfileBufferEntry.h:207
(Diff revision 2)
> +    struct JITFrameData {
> +      bool operator==(const JITFrameData& aOther) const;
> +      bool operator!=(const JITFrameData& aOther) const { return !(*this == aOther); }
> +
> +      JITAddress mJITAddress;
> +      uint32_t mJITDepth;

Do these names still need "JIT" in them?

::: tools/profiler/core/ProfileBufferEntry.cpp:306
(Diff revision 2)
> +UniqueStacks::FrameKey::Hash() const
> +{
> +  uint32_t hash = 0;
> +  if (mData.is<NormalFrameData>()) {
> +    const NormalFrameData& data = mData.as<NormalFrameData>();
> +    hash = AddToHash(hash, 0);

At this point `hash` is 0. Why are you hashing 0 with 0? Just remove this?

::: tools/profiler/core/ProfileBufferEntry.cpp:318
(Diff revision 2)
> -    if (mJITDepth.isSome()) {
> -      hash = AddToHash(hash, *mJITDepth);
>      }
> +  } else {
> +    const JITFrameData& data = mData.as<JITFrameData>();
> +    hash = AddToHash(hash, 1);

Why hash the 1? Seems pointless.

::: tools/profiler/core/ProfileBufferEntry.cpp:431
(Diff revision 2)
>  
>  void
>  UniqueStacks::StreamNonJITFrame(const FrameKey& aFrame)
>  {
> +  using NormalFrameData = FrameKey::NormalFrameData;
> +  MOZ_RELEASE_ASSERT(aFrame.mData.is<NormalFrameData>());

Variant::is() already contains a release assertion of this form, so you can remove this one.
Attachment #8951921 - Flags: review?(n.nethercote) → review+
Comment on attachment 8951922 [details]
Bug 1429904 - Tell the ProfiledThreadData what the buffer position was when the thread received its JSContext.

https://reviewboard.mozilla.org/r/221186/#review227098
Attachment #8951922 - Flags: review?(n.nethercote) → review+
Comment on attachment 8951923 [details]
Bug 1429904 - Give UniqueJSONStrings a copy constructor.

https://reviewboard.mozilla.org/r/221188/#review227104
Attachment #8951923 - Flags: review?(n.nethercote) → review+
Comment on attachment 8951924 [details]
Bug 1429904 - Put mUniqueStrings into a UniquePtr.

https://reviewboard.mozilla.org/r/221190/#review227106
Attachment #8951924 - Flags: review?(n.nethercote) → review+
Comment on attachment 8951927 [details]
Bug 1429904 - Remove unused arguments and return values.

https://reviewboard.mozilla.org/r/221196/#review227108
Attachment #8951927 - Flags: review?(n.nethercote) → review+
Comment on attachment 8951930 [details]
Bug 1429904 - Remove ProfileBuffer::Reset().

https://reviewboard.mozilla.org/r/221204/#review227110
Attachment #8951930 - Flags: review?(n.nethercote) → review+
Comment on attachment 8951925 [details]
Bug 1429904 - Add JITFrameInfo.

https://reviewboard.mozilla.org/r/221192/#review227242

::: tools/profiler/core/ProfileBufferEntry.h:158
(Diff revision 3)
> +struct JITFrameInfoForBufferRange final
> +{
> +  JITFrameInfoForBufferRange Clone() const;
> +
> +  uint64_t mRangeStart;
> +  uint64_t mRangeEnd;

Please add a comment to say if this marks the last valid index or the first invalid index.

::: tools/profiler/core/ProfileBufferEntry.h:186
(Diff revision 3)
> +
> +  // Creates a new JITFrameInfoForBufferRange object in mRanges by looking up
> +  // information about the provided JIT return addresses using aCx.
> +  // Addresses are provided like this:
> +  // The caller of AddInfoForRange supplies a function in aJITAddressProvider.
> +  // This function will be called once, synchrously, with an aJITAddressConsumer

typo: "synchronously"

::: tools/profiler/core/ProfileBufferEntry.h:197
(Diff revision 3)
> +  // Returns whether the information stored in this object is still relevant
> +  // for any entries in the buffer.
> +  bool HasExpired(uint64_t aCurrentBufferRangeStart) const
> +  {
> +    if (mRanges.IsEmpty()) {
> +      return true;

It feels to me like this should be `return false`. I could be wrong, but a comment would be helpful.

::: tools/profiler/core/ProfileBufferEntry.cpp:646
(Diff revision 3)
>                                      aJITFrame);
>        });
>    }
>  }
>  
> +namespace {

Why the anonymous namespace here?

::: tools/profiler/core/ProfileBufferEntry.cpp:1104
(Diff revision 3)
> +  MOZ_RELEASE_ASSERT(aContext);
> +
> +  aRangeStart = std::max(aRangeStart, mRangeStart);
> +  aJITFrameInfo.AddInfoForRange(aRangeStart, mRangeEnd, aContext,
> +    [&](std::function<void(void*)> aJITAddressConsumer) {
> +      // Find all JitReturnAddr entries in the given range for the given thread,

The indenting should be 2 here, not 4. (I know the aJitAddressConsumer line complicates things... what does clang-format do with that line?)

::: tools/profiler/core/ProfileBufferEntry.cpp:1133
(Diff revision 3)
> +            aJITAddressConsumer(e.Get().u.mPtr);
> +          }
> +          e.Next();
> +        }
> +      }
> +    });

I found this loop hard to read, particularly with all the `e.Has()` calls. For complex loops like this I always start with `while (true)` (or `loop` in Rust) and use `break` for exit conditions.

I tried doing this and ended up with the following.
```
  while (true) {
    // Find the next ThreadId entry.
    while (true) {
      if (!e.Has()) {
        goto end_of_outer_loop;
      }
      if (e.Get().IsThreadId()) {
        break;
      }
      e.Next();
    } 
    
    e.Next();
    int threadId = e.Get().u.mInt;
      
    // Ignore samples that are for a different thread.
    if (threadId != aThreadId) {
      continue;
    }   
      
    // Process all JitReturnAddrs up to the next ThreadId entry.
    while (true) {
      if (!e.Has()) {
        goto end_of_outer_loop;
      }
      if (e.Get().IsThreadId()) {
        break;
      }
      if (e.get().IsJitReturnAddr()) {
        aJITAddressConsumer(e.Get().u.mPtr);
      }
      e.Next();
    } 
  } 
end_of_outer_loop:
```

It's not as much of an improvement as I hoped, though it's nice that the two inner loops match each other more closely. I'll let you choose which version to use.
Attachment #8951925 - Flags: review?(n.nethercote) → review+
You could also remove the threadId check between the inner loops and move it into the second inner loops, like so:

>       if (e.get().IsJitReturnAddr() && threadId == aThreadId) {

Again, I'm not sure if that's better.
Comment on attachment 8951926 [details]
Bug 1429904 - When a JSContext for a thread is about to go away, collect enough information about any JIT entries in the buffer so that the entire buffer can be streamed to JSON.

https://reviewboard.mozilla.org/r/221194/#review227336

I like how many lines of code this patch removes.

::: commit-message-467e7:24
(Diff revision 3)
> +This new way of doing things has multiple advantages:
> + - We no longer reset the buffer, so we no longer lose information about other
> +   threads.
> + - All threads from a given process now always have sample data for the same
> +   time range. Before this change, the "partial profile" from a thread that
> +   lost its JSContext could extend further into the past further than the other

"further" is repeated.

::: tools/profiler/core/ProfileBufferEntry.h
(Diff revision 3)
> -  // The JITAddress struct packages the two together and can be used as a hash
> -  // key.
> -  struct JITAddress
> -  {
> -    void* mAddress;
> -    uint32_t mStreamingGen;

Nice to see another generation tracker removed!

::: tools/profiler/core/ProfileBufferEntry.cpp:374
(Diff revision 3)
>      if (data.mCategory.isSome()) {
>        hash = AddToHash(hash, *data.mCategory);
>      }
>    } else {
>      const JITFrameData& data = mData.as<JITFrameData>();
>      hash = AddToHash(hash, 1);

I still don't think this AddToHash() is necessary :)

::: tools/profiler/core/ProfileBufferEntry.cpp:412
(Diff revision 3)
>  
> -MOZ_MUST_USE nsTArray<UniqueStacks::FrameKey>
> -UniqueStacks::GetOrAddJITFrameKeysForAddress(JSContext* aContext,
> -                                             const JITAddress& aJITAddress)
> -{
> -  nsTArray<FrameKey>& frameKeys =
> +template<typename RangeT, typename PosT>
> +struct PositionInRangeComparator final
> +{
> +  bool Equals(const RangeT& aRange, PosT aPos) const
> +  { return aRange.mRangeStart <= aPos && aPos < aRange.mRangeEnd; }

Yukky formatting. Standard style would put the braces on their own lines.

::: tools/profiler/core/ProfileBufferEntry.cpp:415
(Diff revision 3)
> -    }
> -    MOZ_ASSERT(frameKeys.Length() > 0);
> -  }
>  
> -  // Return a copy of the array.
> -  return nsTArray<FrameKey>(frameKeys);
> +  bool LessThan(const RangeT& aRange, PosT aPos) const
> +  { return aRange.mRangeEnd <= aPos; }

ditto
Attachment #8951926 - Flags: review?(n.nethercote) → review+
Attachment #8951919 - Attachment is obsolete: true
Comment on attachment 8951921 [details]
Bug 1429904 - Use a Variant to split the FrameKey members into two groups.

https://reviewboard.mozilla.org/r/221184/#review227094

> Do these names still need "JIT" in them?

Good point. Removed.

> At this point `hash` is 0. Why are you hashing 0 with 0? Just remove this?

Removed. I wanted to AddToHash(hash, mData.tag), and this was the explicit way of doing that, but I realize now that there's not much value in doing that.

> Variant::is() already contains a release assertion of this form, so you can remove this one.

Perfect!
Comment on attachment 8951925 [details]
Bug 1429904 - Add JITFrameInfo.

https://reviewboard.mozilla.org/r/221192/#review227242

> Please add a comment to say if this marks the last valid index or the first invalid index.

Done. (It's the first invalid index. I can't recall the last time I used a range where the end was inclusive.)

> typo: "synchronously"

Haha, oops

> It feels to me like this should be `return false`. I could be wrong, but a comment would be helpful.

It doesn't really matter much. I've added a comment.

> Why the anonymous namespace here?

Cargo-culted from other JSONWriteFunc implementations. I've removed the namespace.

> The indenting should be 2 here, not 4. (I know the aJitAddressConsumer line complicates things... what does clang-format do with that line?)

clang-format seems to agree with me. Here's what it had to suggest:

   aRangeStart = std::max(aRangeStart, mRangeStart);
-  aJITFrameInfo.AddInfoForRange(aRangeStart, mRangeEnd, aContext,
+  aJITFrameInfo.AddInfoForRange(
+    aRangeStart,
+    mRangeEnd,
+    aContext,
     [&](std::function<void(void*)> aJITAddressConsumer) {
       // Find all JitReturnAddr entries in the given range for the given thread,
       // and call aJITAddressConsumer with those addresses.

> I found this loop hard to read, particularly with all the `e.Has()` calls. For complex loops like this I always start with `while (true)` (or `loop` in Rust) and use `break` for exit conditions.
> 
> I tried doing this and ended up with the following.
> ```
>   while (true) {
>     // Find the next ThreadId entry.
>     while (true) {
>       if (!e.Has()) {
>         goto end_of_outer_loop;
>       }
>       if (e.Get().IsThreadId()) {
>         break;
>       }
>       e.Next();
>     } 
>     
>     e.Next();
>     int threadId = e.Get().u.mInt;
>       
>     // Ignore samples that are for a different thread.
>     if (threadId != aThreadId) {
>       continue;
>     }   
>       
>     // Process all JitReturnAddrs up to the next ThreadId entry.
>     while (true) {
>       if (!e.Has()) {
>         goto end_of_outer_loop;
>       }
>       if (e.Get().IsThreadId()) {
>         break;
>       }
>       if (e.get().IsJitReturnAddr()) {
>         aJITAddressConsumer(e.Get().u.mPtr);
>       }
>       e.Next();
>     } 
>   } 
> end_of_outer_loop:
> ```
> 
> It's not as much of an improvement as I hoped, though it's nice that the two inner loops match each other more closely. I'll let you choose which version to use.

Yeah, I had a hard time coming up with something readable here and went back and forth between a few different solutions multiple times.

Thanks for taking the time to suggest a different version, but I don't really find it easier to understand. It also has a bug (extraneous e.Next() call before int threadId).

I've changed the outer "while (e.Has())" to a "for (;;)" because e.Has() is checked right afterwards anyways. That at least gets rid of one of the many e.Has() calls. I've kept the rest of my implementation as is.

I also experimented with adding an EntryGetter::AdvanceUntil method that takes a lambda, but C++'s lambdas take up too much space for this to actually make a positive difference in readability: https://pastebin.mozilla.org/9078763
Comment on attachment 8954651 [details]
Bug 1429904 - Remove unused operator< from StackKey and FrameKey.

https://reviewboard.mozilla.org/r/223738/#review229782
Attachment #8954651 - Flags: review?(n.nethercote) → review+
Comment on attachment 8954652 [details]
Bug 1429904 - Remove mHash which is now unused. It was only used by FrameKey::operator<.

https://reviewboard.mozilla.org/r/223740/#review229784
Attachment #8954652 - Flags: review?(n.nethercote) → review+
Comment on attachment 8954653 [details]
Bug 1429904 - Let the compiler implement the FrameKey copy constructor for us.

https://reviewboard.mozilla.org/r/223742/#review229786
Attachment #8954653 - Flags: review?(n.nethercote) → review+
Comment on attachment 8954654 [details]
Bug 1429904 - Add another constructor to FrameKey which initializes mLine and mCategory, and use it in one place.

https://reviewboard.mozilla.org/r/223744/#review229788
Attachment #8954654 - Flags: review?(n.nethercote) → review+
Comment on attachment 8954655 [details]
Bug 1429904 - Make FrameKey members const.

https://reviewboard.mozilla.org/r/223746/#review229790
Attachment #8954655 - Flags: review?(n.nethercote) → review+
Comment on attachment 8954656 [details]
Bug 1429904 - Remove a comment about std::string.

https://reviewboard.mozilla.org/r/223748/#review229792
Attachment #8954656 - Flags: review?(n.nethercote) → review+
> I've changed the outer "while (e.Has())" to a "for (;;)"

I have a strong personal preference for `while (true)`; I find it much clearer than `for (;;)`. But suit yourself...
Changed to while (true).
Comment on attachment 8951923 [details]
Bug 1429904 - Give UniqueJSONStrings a copy constructor.

https://reviewboard.mozilla.org/r/221188/#review229948


Code analysis found 1 defect in this patch:
 - 1 defect found by clang-tidy

You can run this analysis locally with:
 - `./mach static-analysis check path/to/file.cpp` (C/C++)


If you see a problem in this automated review, please report it here: http://bit.ly/2y9N9Vx


::: tools/profiler/core/ProfileBufferEntry.cpp:248
(Diff revision 6)
>  MakeForEachTrackedOptimizationAttemptsLambdaOp(LambdaT&& aLambda)
>  {
>    return ForEachTrackedOptimizationAttemptsLambdaOp<LambdaT>(Move(aLambda));
>  }
>  
> +UniqueJSONStrings::UniqueJSONStrings()

Warning: Use '= default' to define a trivial default constructor [clang-tidy: modernize-use-equals-default]
Pushed by mstange@themasta.com:
https://hg.mozilla.org/integration/autoland/rev/e270b4748814
Remove unused operator< from StackKey and FrameKey. r=njn
https://hg.mozilla.org/integration/autoland/rev/39a1b2434439
Remove mHash which is now unused. It was only used by FrameKey::operator<. r=njn
https://hg.mozilla.org/integration/autoland/rev/595906406c16
Let the compiler implement the FrameKey copy constructor for us. r=njn
https://hg.mozilla.org/integration/autoland/rev/377750a82bf6
Add another constructor to FrameKey which initializes mLine and mCategory, and use it in one place. r=njn
https://hg.mozilla.org/integration/autoland/rev/43208c534673
Make FrameKey members const. r=njn
https://hg.mozilla.org/integration/autoland/rev/58fdb209349c
Remove a comment about std::string. r=njn
https://hg.mozilla.org/integration/autoland/rev/7e25215922b8
Add 'using namespace mozilla;' to ProfileBufferEntry.cpp and remove some mozilla:: prefixes. r=njn
https://hg.mozilla.org/integration/autoland/rev/b463b8aeb9f3
Use a Variant to split the FrameKey members into two groups. r=njn
https://hg.mozilla.org/integration/autoland/rev/4d359db9dcf6
Tell the ProfiledThreadData what the buffer position was when the thread received its JSContext. r=njn
https://hg.mozilla.org/integration/autoland/rev/4ade11251223
Give UniqueJSONStrings a copy constructor. r=njn
https://hg.mozilla.org/integration/autoland/rev/11e6e1eb039a
Put mUniqueStrings into a UniquePtr. r=njn
https://hg.mozilla.org/integration/autoland/rev/34f128690886
Add JITFrameInfo. r=njn
https://hg.mozilla.org/integration/autoland/rev/544e3884d895
When a JSContext for a thread is about to go away, collect enough information about any JIT entries in the buffer so that the entire buffer can be streamed to JSON. r=njn
https://hg.mozilla.org/integration/autoland/rev/8cbd19b941a0
Remove unused arguments and return values. r=njn
https://hg.mozilla.org/integration/autoland/rev/e41dd572e115
Remove ProfileBuffer::Reset(). r=njn
Also, there were other failures like:

GTEST - application crashed [@ JITFrameInfo::AddInfoForRange
Log: https://treeherder.mozilla.org/logviewer.html#?job_id=165052839&repo=autoland&lineNumber=1464

DT -  Main app process exited normally | application crashed [@ JITFrameInfo::AddInfoForRange] 
Log: https://treeherder.mozilla.org/logviewer.html#?job_id=165025665&repo=autoland&lineNumber=3738
Comment on attachment 8951923 [details]
Bug 1429904 - Give UniqueJSONStrings a copy constructor.

https://reviewboard.mozilla.org/r/221188/#review229948

> Warning: Use '= default' to define a trivial default constructor [clang-tidy: modernize-use-equals-default]

This is https://github.com/mozilla-releng/services/issues/888
Comment on attachment 8951923 [details]
Bug 1429904 - Give UniqueJSONStrings a copy constructor.

https://reviewboard.mozilla.org/r/221188/#review230414


Code analysis found 1 defect in this patch:
 - 1 defect found by clang-tidy

You can run this analysis locally with:
 - `./mach static-analysis check path/to/file.cpp` (C/C++)


If you see a problem in this automated review, please report it here: http://bit.ly/2y9N9Vx


::: tools/profiler/core/ProfileBufferEntry.cpp:248
(Diff revision 7)
>  MakeForEachTrackedOptimizationAttemptsLambdaOp(LambdaT&& aLambda)
>  {
>    return ForEachTrackedOptimizationAttemptsLambdaOp<LambdaT>(Move(aLambda));
>  }
>  
> +UniqueJSONStrings::UniqueJSONStrings()

Warning: Use '= default' to define a trivial default constructor [clang-tidy: modernize-use-equals-default]
I've changed the std::function arguments to take const references, and AddJITFrameInfo to just return if it's called with an empty range.
Flags: needinfo?(mstange)
Pushed by mstange@themasta.com:
https://hg.mozilla.org/integration/autoland/rev/f96a75472e04
Remove unused operator< from StackKey and FrameKey. r=njn
https://hg.mozilla.org/integration/autoland/rev/f1408af12dfa
Remove mHash which is now unused. It was only used by FrameKey::operator<. r=njn
https://hg.mozilla.org/integration/autoland/rev/bad1b732f5f9
Let the compiler implement the FrameKey copy constructor for us. r=njn
https://hg.mozilla.org/integration/autoland/rev/4559c0d9ff0e
Add another constructor to FrameKey which initializes mLine and mCategory, and use it in one place. r=njn
https://hg.mozilla.org/integration/autoland/rev/b465484b85ab
Make FrameKey members const. r=njn
https://hg.mozilla.org/integration/autoland/rev/0bf304822495
Remove a comment about std::string. r=njn
https://hg.mozilla.org/integration/autoland/rev/c072bfec3cd0
Add 'using namespace mozilla;' to ProfileBufferEntry.cpp and remove some mozilla:: prefixes. r=njn
https://hg.mozilla.org/integration/autoland/rev/5438aa8b248a
Use a Variant to split the FrameKey members into two groups. r=njn
https://hg.mozilla.org/integration/autoland/rev/099e70b23f74
Tell the ProfiledThreadData what the buffer position was when the thread received its JSContext. r=njn
https://hg.mozilla.org/integration/autoland/rev/d3a10f0b9df6
Give UniqueJSONStrings a copy constructor. r=njn
https://hg.mozilla.org/integration/autoland/rev/b323ee89d4c7
Put mUniqueStrings into a UniquePtr. r=njn
https://hg.mozilla.org/integration/autoland/rev/3284f277d532
Add JITFrameInfo. r=njn
https://hg.mozilla.org/integration/autoland/rev/0d15c7f90536
When a JSContext for a thread is about to go away, collect enough information about any JIT entries in the buffer so that the entire buffer can be streamed to JSON. r=njn
https://hg.mozilla.org/integration/autoland/rev/14627f6eefd1
Remove unused arguments and return values. r=njn
https://hg.mozilla.org/integration/autoland/rev/39df0ca8a664
Remove ProfileBuffer::Reset(). r=njn
You need to log in before you can comment on or make changes to this bug.