Bug 1116188 - [e10s] Stop using sync messages for Gecko profiler
How do we make this async?

Well, let's see what's sync...

Here's where I added profile powers for the content and plugin processes. (Bug 1008435).

It looks like the Profiler backend fires an observer notification when it wants the profiles from each process.

So that's synchronous. That's no good. I should find where that observer notification is fired… void TableTicker::StreamJSObject seems to be the place.

Ah, nsIProfiler's GetProfile is sync. So that's one part of the problem.

We'll probably need an async method here...

So there are a few pieces here.

I need to make nsIProfiler have an async method for gathering profiles, and I need that method to asynchronously gather profiles from each process.

Then I need to alter Gecko-Profiler-Addon to use that method for gathering profiles. Luckily, it looks like Gecko-Profiler-Addon already has a callback-y interface. So that'll be easy!

So the real challenge is the nsIProfiler bit.

Let's pretend I add a method called GetProfileAsync that takes some callback thing. The callback will be called with the profile when it's ready.

GetProfileAync

Interestingly, the profiler-subprocess nsIObserverNotification subject is a ProfileSaveEvent, which takes a SubProcessCallback… and so it looks like there's already some async machinery here...

Hm.. I'm not convinced that this SubProcessCallback and SubProcessContext thing is going to work… I think it's assumed, when sewing together this profile, that it's all going to come in synchronously. That really isn't going to be the case.

Well… unless it's known that we don't complete the profile string until the children all respond somehow. Like, the other processes should say "Here's my profile", or maybe "I have no profile, because I'm not profiling" (or maybe just an empty profile).

Like, the main thread holds onto the current progress of sewing together the profile… exits and stashes current state when it reaches the point where we need subprocess stuff… when subprocesses report in, add them in the order they come in.

Also, we might have to break up the profile message. It can be several megabytes… I think we can hit limits. Like:

WARNING: IPC message is too big: file /Users/mikeconley/Projects/mozilla-central/ipc/chromium/src/chrome/common/ipc_channel_posix.cc, line 531

Wait… the message limit is, apparently, 268435456 bytes… or 268.435 MB. That's crazy if we exceed that. :/

Something really funky is going down with my OS X build. :( Content process keeps crashing when I try to grab a profile. Why?

Markus has pointed me to this work which is relevant… " Bug 1154115 - Rewrite profile JSON streaming". Anything I do should probably be based on this.

THE PLAN:
  1. Have GetProfile and GetProfileData both return Promises (ensure we have JS Contexts, and use Promise::Create, just like ServiceWorkerManager:

nsCOMPtr < nsIGlobalObject > sgo = do_QueryInterface(window);
ErrorResult result;
nsRefPtr < Promise > promise = Promise :: Create(sgo, result);
if (result.Failed()) {
return result.StealNSResult();
}

ServiceWorkerJobQueue * queue = GetOrCreateJobQueue(cleanedScope);
MOZ_ASSERT(queue);

nsRefPtr < ServiceWorkerResolveWindowPromiseOnUpdateCallback > cb =
new ServiceWorkerResolveWindowPromiseOnUpdateCallback(window, promise);

nsRefPtr < ServiceWorkerRegisterJob > job =
new ServiceWorkerRegisterJob(queue, cleanedScope, spec, cb, documentPrincipal);
queue -> Append(job);

promise.forget(aPromise);
  1. This function should through if a Profile is already being gathered. Maybe expose some state on nsIProfiler to say whether gathering is underway.
  2. Have nsProfiler or what-have-you send the profiler-subprocess notification around. That should immediately return with a count of how many profiles we're waiting for. Stash that value somewhere in nsProfiler.
  3. Each process parent asks each child for their profiles.
  4. Each child receives message, gathers profile, send async message with the result.
  5. Each parent receives result, calls into nsProfiler with the result.
  6. nsProfiler receives result. Decrements counter. If the counter is at 0, finish the stream and resolve the promise. Or, if the result is an error, Reject the promise.

That's the general idea of it, anyhow.

Bah. Shu's patch in bug 1154115 doesn't apply cleanly. :(

Idea: pass object through observer notification. Has method I call which returns function to call when message with profile returns.

Class maintains count of functions it has handed out, is in charge of resolving Promise once last Profile comes in?

Ok, Shu's patch has landed. Time to unmothball this bug.

Let's see if I can just return a Promise that I can resolve after some time...

We're going to make it only possible to get profiles from other processes asynchronously. Are you ready?


Assertion failure: !PreservingWrapper() (Destroying cache with a preserved wrapper!), at /Users/mikeconley/Projects/mozilla-central/dom/base/nsWrapperCache.h:80
#01: nsWrapperCache::~nsWrapperCache()[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x107a17f]
#02: mozilla::dom::Promise::~Promise()[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x37697d4]
#03: mozilla::dom::Promise::~Promise()[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x376abb5]
#04: mozilla::dom::Promise::~Promise()[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x376ab89]
#05: mozilla::dom::Promise::DeleteCycleCollectable()[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x376aaee]
#06: mozilla::dom::Promise::cycleCollection::DeleteCycleCollectable(void*)[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x3777ec2]
#07: SnowWhiteKiller::~SnowWhiteKiller()[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0xcf692]
#08: RemoveSkippableVisitor::~RemoveSkippableVisitor()[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0xd0de8]
#09: RemoveSkippableVisitor::~RemoveSkippableVisitor()[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0xbab55]
#10: nsPurpleBuffer::RemoveSkippable(nsCycleCollector*, bool, bool, void (*)())[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0xad14a]
#11: nsCycleCollector::ForgetSkippable(bool, bool)[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0xad4ae]
#12: nsCycleCollector_forgetSkippable(bool, bool)[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0xb153e]
#13: FireForgetSkippable(unsigned int, bool)[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x1ba629b]
#14: CCTimerFired(nsITimer*, void*)[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x1ba78b5]
#15: nsTimerImpl::Fire()[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x19caeb]
#16: nsTimerEvent::Run()[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x19ced1]
#17: nsThread::ProcessNextEvent(bool, bool*)[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x197966]
#18: NS_ProcessNextEvent(nsIThread*, bool)[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x1f2b88]
#19: nsThread::Shutdown()[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x196f85]
#20: mozilla::LazyIdleThread::ShutdownThread()[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x18de1e]
#21: mozilla::LazyIdleThread::Notify(nsITimer*)[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x18ec28]
#22: non-virtual thunk to mozilla::LazyIdleThread::Notify(nsITimer*)[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x18eccf]
#23: nsTimerImpl::Fire()[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x19cb07]
#24: nsTimerEvent::Run()[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x19ced1]
#25: nsThread::ProcessNextEvent(bool, bool*)[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x197966]
#26: NS_ProcessNextEvent(nsIThread*, bool)[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x1f2b88]
#27: nsThread::Shutdown()[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x196f85]
#28: void nsRunnableMethodArguments<>::apply<nsIThread, nsresult (nsIThread::*)()>(nsIThread*, nsresult (nsIThread::*)())[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x1a4013]
#29: nsRunnableMethodImpl<nsresult (nsIThread::*)(), true>::Run()[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x1a3caa]
#30: nsThread::ProcessNextEvent(bool, bool*)[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x197966]
#31: NS_ProcessNextEvent(nsIThread*, bool)[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x1f2b88]
#32: nsThread::Shutdown()[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x196f85]
#33: void nsRunnableMethodArguments<>::apply<nsIThread, nsresult (nsIThread::*)()>(nsIThread*, nsresult (nsIThread::*)())[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x1a4013]
#34: nsRunnableMethodImpl<nsresult (nsIThread::*)(), true>::Run()[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x1a3caa]
#35: nsThread::ProcessNextEvent(bool, bool*)[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x197966]
#36: NS_ProcessNextEvent(nsIThread*, bool)[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x1f2b88]
#37: nsThread::Shutdown()[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x196f85]
#38: void nsRunnableMethodArguments<>::apply<nsIThread, nsresult (nsIThread::*)()>(nsIThread*, nsresult (nsIThread::*)())[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x1a4013]
#39: nsRunnableMethodImpl<nsresult (nsIThread::*)(), true>::Run()[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x1a3caa]
#40: nsThread::ProcessNextEvent(bool, bool*)[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x197966]
#41: NS_ProcessPendingEvents(nsIThread*, unsigned int)[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x1f29cb]
#42: nsBaseAppShell::NativeEventCallback()[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x39c8ac9]
#43: nsAppShell::ProcessGeckoEvents(void*)[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x3a46f31]
#44: __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__[/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation +0x12b31]
#45: __CFRunLoopDoSources0[/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation +0x12455]
#46: __CFRunLoopRun[/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation +0x357f5]
#47: CFRunLoopRunSpecific[/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation +0x350e2]
#48: RunCurrentEventLoopInMode[/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox +0x5feb4]
#49: ReceiveNextEventCommon[/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox +0x5fc52]
#50: BlockUntilNextEventMatchingListInMode[/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/HIToolbox +0x5fae3]
#51: _DPSNextEvent[/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit +0x155533]
#52: -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:][/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit +0x154df2]
#53: -[GeckoNSApplication nextEventMatchingMask:untilDate:inMode:dequeue:][/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x3a45b57]
#54: -[NSApplication run][/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit +0x14c1a3]
#55: nsAppShell::Run()[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x3a478dc]
#56: nsAppStartup::Run()[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x4991f21]
#57: XREMain::XRE_mainRun()[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x4a45c4c]
#58: XREMain::XRE_main(int, char**, nsXREAppData const*)[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x4a465ae]
#59: XRE_main[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/XUL +0x4a46a5d]
#60: do_main(int, char**, nsIFile*)[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/firefox +0x1cb5]
#61: main[/Users/mikeconley/Projects/mozilla-central/obj-debug/dist/NightlyDebug.app/Contents/MacOS/firefox +0x1161]

So I've got this Promise thing… how should this work?

Pass this thing that implements nsISupports. Passes it through the observer service to all of the parents. Parents that can profile increment the counter and get the callback. Individually store the callbacks. Sends message to child to get profile. Message comes back from child, grabs stashed callback, and calls it with result. Should assert main thread.

Simplest, after talking to jrmuizel, ehsan, mstange and tbsaunde - instead of doing fancy things where I call a method and get back a callable, I'll just have the Parent's stash the thing I'm passing around the observer service.

MOZ_ASSERT(NS_IsMainThread());

if (NS_WARN_IF(!aCx)) {
return NS_ERROR_FAILURE;
}

JSObject *jsGlobal = JS::CurrentGlobalOrNull(aCx);

if (NS_WARN_IF(!jsGlobal)) {
return NS_ERROR_FAILURE;
}

nsIScriptGlobalObject* sgo = nsJSUtils::GetStaticScriptGlobal(jsGlobal);

if (NS_WARN_IF(!sgo)) {
return NS_ERROR_FAILURE;
}

ErrorResult result;
nsRefPtr<Promise> promise = Promise::Create(sgo, result);
if (result.Failed()) {
return result.StealNSResult();
}

promise->MaybeResolve(true);

promise.forget(aPromise);
return NS_OK;

Create the promise in nsProfiler, and pass it along to TableTicker for resolution?
Create a new class that implements nsISupports called, uh, ProfileGatherer? Make sure we can send it around when we send the observer notification.

This thing should probably take a TableTicker, a SpliceableJSONWriter, and the Promise to be resolved.

Methods for the start and end of creating the profile. Make the sync stuff call the first, and the latter - remove the profile-subprocess stuff from it.

For the async stuff, call the start, and then do our special monitoring stuff. Once the last oop profile comes in, do the end stuff, and then pass it to the promise resolver.

It should immediately start blasting out the system stuff.

Split and move TableTicker::StreamJSON logic into two TableTicker static methods - Head and Foot

Ok, so I've got like… the very bare minimum written to maybe get this to work. And it's still kinda busted. I'm crashing for some reason when I try to gather the profile, and neither gdb nor XCode are being very forthcoming about why. It's really frustrating - like, I don't seem to hit a breakpoint.

Huh… something to do with the JSON writer...

Why am I crashing? Why is resolving the Promise causing us to fail this assertion:

Assertion failure: cx->runtime()->requestDepth || cx->runtime()->isHeapBusy(), at /Users/mikeconley/Projects/mozilla-central/js/src/jscntxt.cpp:1172

What is an AutoJSContext? Could that help me here?

13:33 (shu) mconley|livehacking: oh i don't know anything about the C++ Promise crap either :/
13:33 (shu) mconley|livehacking: but yeah, try AutoJSContext on the stack
13:33 (mconley|livehacking) alright, I'll look into that. Thanks!
13:35 Waldo has joined (waldo@ moz-h28sem.sntcca.sbcglobal.net )
13:37 (sfink) mconley|livehacking: and if that doesn't work, try an AutoJSAPI - https://dxr.mozilla.org/mozilla-central/source/dom/base/ScriptSettings.h?from=AutoJSAPI#213
13:37 (efaust) "..better add a test for legacy generators, just in case", they said. "Who the hell uses |new| on a legacy generator?", he said. "That's some mighty fine test failure you've got there", the sheriff said.
13:38 (mconley|livehacking) sfink: ooh, thank you!
13:38 (Waldo) efaust: lol
13:38 (sfink) mconley|livehacking: or maybe AutoEntryScript if whatever you're doing might run a JS script inside there

I'm failing this assertion now...
MOZ_ASSERT(strlen(mChunkList[i].get()) == mChunkLengths[i]);

HOLY SHIT. WE DID IT.

Apparently, I need to enter a compartment in order to exercise the JSAPI. The JSContext I was using was garbage, and I needed to get into this unprivileged junk compartment. Otherwise, I had no zone. Something like that.

Before:

void
ProfileGatherer
:: Resolve()
{
MOZ_ASSERT(NS_IsMainThread());
AutoJSContext cx;
JS
:: RootedValue val(cx);
{
UniquePtr
< char [] > buf = mWriter.WriteFunc() -> CopyData();
NS_ConvertUTF8toUTF16
js_string (nsDependentCString(buf.get()));
MOZ_ALWAYS_TRUE(JS_ParseJSON(cx,
static_cast < const char16_t *> (js_string.get()),
js_string.Length(),
& val));
}
mPromise
-> MaybeResolve(cx, val);
}

After:

void
ProfileGatherer
:: Resolve()
{
MOZ_ASSERT(NS_IsMainThread());
AutoJSAPI jsapi;
jsapi.Init();
JSContext
* cx = jsapi.cx();
JSAutoCompartment
ac (cx, xpc :: UnprivilegedJunkScope());

JS
:: RootedValue val(cx);
{
UniquePtr
< char [] > buf = mWriter.WriteFunc() -> CopyData();
NS_ConvertUTF8toUTF16
js_string (nsDependentCString(buf.get()));
MOZ_ALWAYS_TRUE(JS_ParseJSON(cx,
static_cast < const char16_t *> (js_string.get()),
js_string.Length(),
& val));
}
mPromise
-> MaybeResolve(val);
}


Reconsider destructor mechanism

Naw - I don't think I want to do this, because having the TableTicker hold a reference to the gatherer is important so that we know that we can't gather another set of profiles while already gathering one. If that makes any sense.

Clear out TableTicker's mGatherer on Resolving...
Why am I not getting profiles from the plugin process?

That was me being foolish - I'd forgotten that if the plugin process starts _after_ I've begun profiling, then I won't get a profile from it.

Make sure that nsIProfiler.getProfileDataAsync works from the Profiler add-on

Hrm - right now, this doesn't work because I can't seem to get the ScriptGlobalObject with:

MOZ_ASSERT(NS_IsMainThread());

if (NS_WARN_IF( ! aCx)) {
return NS_ERROR_FAILURE;
}

JSObject
* jsGlobal = JS :: CurrentGlobalOrNull(aCx);

if (NS_WARN_IF( ! jsGlobal)) {
return NS_ERROR_FAILURE;
}

nsIScriptGlobalObject * sgo = nsJSUtils :: GetStaticScriptGlobal(jsGlobal);

Like, it's null when I call it from the profiler add-on.

Ah! jorendorff helped me find the right incantations to get the nsIGlobalObject out of the JSContext we are passed:

nsIGlobalObject * go = xpc :: NativeGlobal(JS :: CurrentGlobalOrNull(aCx));

Boom! And then I needed to make sure that the object I resolve with resides in the same compartment as the Promise:

AutoJSAPI jsapi;
jsapi.Init();
JSContext * cx = jsapi.cx();
JSAutoCompartment ac (cx, mPromise -> GlobalJSObject());

Aw hell yeah. And we're in business! I'm gathering profiles asynchronously, baby! WOOOOO

Make sure profiler stuff is ifdef'd out where appropriate on MOZ_ENABLE_PROFILER_SPS

BenWa thinks it might be better to do things as follows:

  1. profile-subprocess observer notification goes out. ProfilerGatherer asks all child processes to begin fetching profiles. We pause the profiler here.
  2. Once all profiles come in, ProfileGatherer sends an observer notification out asking for all profiles before blasting out the profile. Don't do the head/foot split.

How hard of a modification is that?

Rebase off of the head/foot bit.
When ToJSONObjectAsync comes in. Create the Gatherer.
Pass around an observer notification for subprocesses to gather their profiles.
In ProfileGatherer, when the count goes to zero, tell TableTicker, which will go about fetching the profiles, and returning a JSObject. Gatherer then calls the callback to resolve the promise.
When ContentParent and PluginModuleParent receive the profiles, stash them in a string.
When the observer notification to get the profiles comes into ContentParent and PluginModuleParent, have them give up their strings and then truncate them
Make sure TableTicker.h and ProfileJSONWriter.h are private
Hook up wiring for a callback to be passed on completion
Have nsProfiler pass a static method callback, and hold on to the Promise for resolution
bz's error handling (see this)
Fix build issue:

/builds/slave/try-lx-00000000000000000000000/build/src/xpcom/base/nsRefPtr.h:66:7: error: invalid use of incomplete type 'class mozilla::ProfileGatherer'
/builds/slave/try-lx-00000000000000000000000/build/src/tools/profiler/TableTicker.h:20:7: error: forward declaration of 'class mozilla::ProfileGatherer'
gmake[5]: *** [platform-linux.o] Error 1
gmake[4]: *** [tools/profiler/target] Error 2
gmake[4]: *** Waiting for unfinished jobs....
gmake[3]: *** [compile] Error 2
gmake[2]: *** [default] Error 2
gmake[1]: *** [realbuild] Error 2
gmake: *** [build] Error 2
Return code: 2
'mach build' did not run successfully. Please check log for errors.
Running post_fatal callback...
Exiting -1
# TBPL FAILURE #
# TBPL FAILURE #

Fix other new build issue
Fix bholley issues

bholley says:

"You should pass your global object directly to jsapi.Init(), and treat it fallibly:

if (!jsapi.Init(myglobal)) {
return false;
}

// No need for a JSAutoCompartment here.


You should also not add new usages of JS_GetPendingException/JS_SetPendingException/JS_ReportPendingException. Use the API on AutoJSAPI instead."

File a bug to use async profile API for Browser Toolbox to get subprocess samples. Filed Bug 1174272