Finally... JavaScript source line info in a dump

For many years, if your C/C++/.NET application had a performance or stability issue (e.g., unresponsiveness, crash, high cpu consumption, etc.), one of the popular - and effective - ways to troubleshoot the problem was to dump the process when the problem symptom surfaced.  And when you open the user mode dump with windbg or cdb, you get a stack trace for each thread in the process.

  • If you have matching private symbols for the native (e.g., non-.NET) assemblies, you'll get source line information for those frames.
  • For frames involving managed assemblies, source line information hasn't been available since the 1.x days.  But you can use commands from SOS such as !u to get close to the line of source executing when the dump was written.

But what if your process is executing JavaScript and you want to understand what line of JavaScript is executing?  For the sake of argument, let's assume you're dealing with IE9 or later where the re-vamped JavaScript engine lives in jscript9.dll.  Matching symbols isn't much help in determining what line of JavaScript code was executing, or even which .js file.  Even if you have private symbols - which, as a Microsoft employee, I have access to - it isn't a straightforward exercise to extract this information.  The following is an example stack of what is revealed.  As you can see, there are no pointers to JavaScript source.

ChildEBP RetAddr 
000b8e7c 7542790d user32!NtUserGetMessage+0x15
000b8e98 6f8b8377 user32!GetMessageW+0x33
000b8f14 6f7f88ca mshtml!InternalShowModalDialog+0x28a
000baf94 6f7f8a6b mshtml!CWindow::ShowHTMLDialogHelper+0x49c
000bb00c 6f8a0883 mshtml!CWindow::showModalDialog+0x60
000bb080 6f54ab59 mshtml!Method_VARIANTp_BSTR_o0oVARIANTp_o0oVARIANTp+0x13d
000bb104 6f436fe3 mshtml!CBase::ContextInvokeEx+0x84c
000bb130 6f4397af mshtml!CSelectionObject::InvokeEx+0x2b
000bb184 6f439c29 mshtml!DispatchInvokeCollection+0x1ab
000bd214 6f458a64 mshtml!CWindow::InvokeEx+0x5ca
000bd240 6f458664 mshtml!CBase::VersionedInvokeEx+0x37
000bd280 6f439142 mshtml!CBase::PrivateInvokeEx+0x82
000bd2ec 6f439204 mshtml!COmWindowProxy::InvokeEx+0x270
000bd314 6f458a64 mshtml!COmWindowProxy::subInvokeEx+0x26
000bd340 6f4392a9 mshtml!CBase::VersionedInvokeEx+0x37
000bd380 580acbb7 mshtml!PlainInvokeEx+0x95
000bd3c8 580b2eeb jscript9!HostDispatch::CallInvokeEx+0x106
000bd3f0 580b2e35 jscript9!HostDispatch::InvokeMarshaled+0x4d
000bd4cc 580b2c50 jscript9!HostDispatch::InvokeByDispId+0x408
000bd4e8 580b2c24 jscript9!DispMemberProxy::DefaultInvoke+0x22
000bd500 580685fe jscript9!DispMemberProxy::DefaultInvoke+0x20
000bd540 580b375a jscript9!Js::JavascriptFunction::CallFunction+0xc4
000bd564 580b3703 jscript9!Js::InterpreterStackFrame::OP_CallFld+0x58
000bd594 580ccadb jscript9!Js::InterpreterStackFrame::Process+0x7ae
000bd5c4 580cca50 jscript9!Js::InterpreterStackFrame::ProcessThunk+0x65
000bd6b0 580685fe jscript9!Js::InterpreterStackFrame::InterpreterThunk+0x21f
000bd6f0 580b375a jscript9!Js::JavascriptFunction::CallFunction+0xc4
000bd714 580b3796 jscript9!Js::InterpreterStackFrame::OP_CallFld+0x58
000bd744 580ccadb jscript9!Js::InterpreterStackFrame::Process+0x7c7
000bd774 580cca50 jscript9!Js::InterpreterStackFrame::ProcessThunk+0x65
000bd838 580685fe jscript9!Js::InterpreterStackFrame::InterpreterThunk+0x21f
000bd86c 58068523 jscript9!Js::JavascriptFunction::CallFunction+0xc4
000bd8d0 5806845a jscript9!Js::JavascriptFunction::CallRootFunction+0xb6
000bd90c 580683e6 jscript9!ScriptSite::CallRootFunction+0x4f
000bd934 58093522 jscript9!ScriptSite::Execute+0x63
000bd9c0 580933bb jscript9!ScriptEngine::ExecutePendingScripts+0x319
000bda64 58094c46 jscript9!ScriptEngine::ParseScriptTextCore+0x33c
000bdab0 6f4f0108 jscript9!ScriptEngine::ParseScriptText+0x67
000bdae8 6f4afb95 mshtml!CActiveScriptHolder::ParseScriptText+0xc7
000bdb58 6f4f108f mshtml!CScriptCollection::ParseScriptText+0x2d2
000bdc3c 6f4f0dde mshtml!CScriptData::CommitCode+0x4ea
000bdc8c 6f331d9e mshtml!CScriptData::Execute+0x1fc
000bdcb8 6f4aff93 mshtml!CScriptData::ScriptData_OnEnterTree+0x240

So if you have a random issue involving JavaScript that can't be reproduced on demand, and you can't repro outside of production, your options of getting a stack of the problem are limited to a dump or a profiler.  But in neither case were you able to easily get source line information for the JavaScript portion of the code.  At least, until now.

 

When you use the latest debugger engine in the Windows 8.1 SDK to create and view a dump of IE11 on Windows 8.1, your stack will show you frames you never saw before.  The numbers at the end of the highlighted frames represent column & row numbers:

ChildEBP RetAddr 
(Inline) -------- jscript9!Js::TaggedInt::IsPair+0x9
0cc6ebb0 074600b9 jscript9!Js::SSE2::JavascriptMath::Modulus_InPlace+0x11
WARNING: Frame IP not in any known module. Following frames may be wrong.
0cc6ec30 64bd4094 js!UTIL.powerLoop1 [https://pmav.eu/stuff/javascript-webworkers/assets/js/javascript-webworkers-v1.4.0.js @ 367,17]
0cc6ef78 64b46548 jscript9!Js::InterpreterStackFrame::Process+0x34b1
0cc6f09c 05d10fc1 jscript9!Js::InterpreterStackFrame::InterpreterThunk<1>+0x1e8
0cc6f0a8 64b4669e js!UTIL.power [https://pmav.eu/stuff/javascript-webworkers/assets/js/javascript-webworkers-v1.4.0.js @ 367,17]
0cc6f3f8 64b46548 jscript9!Js::InterpreterStackFrame::Process+0xbd7
0cc6f53c 05d10fc9 jscript9!Js::InterpreterStackFrame::InterpreterThunk<1>+0x1e8
0cc6f548 64b40685 js!onmessage [https://pmav.eu/stuff/javascript-webworkers/assets/js/javascript-webworkers-v1.4.0.js @ 323,17]
0cc6f590 64b4100e jscript9!Js::JavascriptFunction::CallFunction<1>+0x88
0cc6f5fc 64b40f60 jscript9!Js::JavascriptFunction::CallRootFunction+0x93
0cc6f644 64b40ee7 jscript9!ScriptSite::CallRootFunction+0x42
0cc6f66c 64b4993c jscript9!ScriptSite::Execute+0x6c
0cc6f6c8 64b49878 jscript9!ScriptEngineBase::ExecuteInternal<0>+0xbb
0cc6f6e0 6732b78f jscript9!ScriptEngineBase::Execute+0x1c
0cc6f794 6732b67c mshtml!CListenerDispatch::InvokeVar+0x102
0cc6f7c0 6732b21e mshtml!CListenerDispatch::Invoke+0x61
0cc6f858 6732b385 mshtml!CEventMgr::_InvokeListeners+0x1a2
0cc6f9cc 67245ecc mshtml!CEventMgr::Dispatch+0x35a
0cc6fa08 6721eb22 mshtml!CMessagePort::HandlePostMessage+0x207
0cc6fa10 6721ed2c mshtml!CMessagePort::HandleNotification+0x2e

<clip>

 

If you're not having success seeing these JavaScript frames in your dumps, here's a quick checklist I compiled during my testing.

  • This is not supported on any OS prior to 8.1.
  • This is supported for any process that loads jscript9.dll. This includes processes which host the browser control for IE11. Note I only tested iexplore.exe.
  • You'll need to create *and* view the dump with the debugger engine (used by windbg & cdb) that comes with the 8.1 SDK noted in the link I pasted above. Older versions of the debuggers may not work.
  • Dumps created with DebugDiag v2.0 U1 do not currently yield the JS frames. But I've had luck replacing the dbghelp.dll that ships with DebugDiag with the build that ships with the 8.1 SDK. The next release of DebugDiag is scheduled to include a build of dbghelp.dll that will reveal these JS frames.
  • If you use adplus_old.vbs to create the dump, you may consider making an edit in the file. After you make a copy of the original .vbs, replace all calls to ".dump -u /ma" with "dump /ma".
  • JS frames will not resolve in a live debug. This is currently only supported in a post-mortem dump.
  • Be sure the process you're dumping has JavaScript executing at the time of the dump. Otherwise, you're unlikely to see any of the JS frames.
  • As always, it's a best practice to keep the architectures of the debugger and target aligned:
    • Dump a 32-bit process with a 32-bit debugger, and open the dump with a 32-bit debugger.
    • Dump a 64-bit process with a 64-bit debugger, and open the dump with a 64-bit debugger.
    • With DebugDiag, install the 32-bit version on a 32-bit OS and install the 64-bit version on a 64-bit OS. In the latter case, DebugDiag automagically determines the debugger bitness to use against the target process.
  • If you've followed all the steps above and are still having problems getting the JS frames to display, ensure the following registry keys have these values. 
    • [HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\MiniDumpAuxiliaryDlls, C:\Windows\system32\jscript9.dll = [REG_SZ] C:\Windows\System32\jscript9diag.dll
    • [HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\MiniDumpAuxiliaryDlls], C:\Windows\system32\jscript9.dll = [REG_SZ] C:\Windows\SysWOW64\jscript9diag.dll

 

While the post-mortem debugging experience with JavaScript still isn't as mature as what we've enjoyed for years with .NET or C/C++, the ability to get the source file and line information removes the first significant blocker - now you can easily see what was executing when the process was dumped.