I was trying to use the lldb-dap
dape
config for Emacs. It works well on macOS but freezes on Windows.
If I launch lldb-dap.exe
directly from CMD, keystrokes into the terminal does not show back; and sending EOF (^Z
) does not make it quit on its own either. Killing it with Ctrl-C indicates that no stdin input is being consumed.
>"C:\Program Files\LLVM\bin\lldb-dap.exe"
^C
>test input
'test' is not recognized as an internal or external command,
operable program or batch file.
>Content-Length 123
'Content-Length' is not recognized as an internal or external command,
operable program or batch file.
>^Z
>
Trying to use port communication with "C:\Program Files\LLVM\bin\lldb-dap.exe" -p 6789
and connecting from the same computer yields the following,
PS > Test-NetConnection localhost -port 6789
WARNING: TCP connect to (::1 : 6789) failed
WARNING: TCP connect to (127.0.0.1 : 6789) failed
ComputerName : localhost
RemoteAddress : ::1
RemotePort : 6789
InterfaceAlias : Loopback Pseudo-Interface 1
SourceAddress : ::1
PingSucceeded : True
PingReplyDetails (RTT) : 0 ms
TcpTestSucceeded : False
Any leads would be welcome. Not reporting to the issue tracker yet as I am unsure if it is my setup issue.
If I launch lldb-dap.exe
directly from CMD, keystrokes into the terminal does not show back
lldb-dap.exe does not provide an interactive command-line interface.
Instead, it speaks the “Debug Adapter Protocol” and expects to receive JSON-encoded commands as specified by the Debug Adapter Protocol (DAP) via stdin.
works well on macOS but freezes on Windows.
Probably the best way to debug this would be from within Emacs. Emacs will speak the debug adapter protocol with lldb-dap.exe
. To see where things go wrong, set the LLDBDAP_LOG
environment to a file path. lldb-dap.exe
will then log all commands received from emacs and the corresponding replies to that log file. Based on that, we can then hopefully determine why this is hanging.
Also see Contributing to LLDB-DAP - 🐛 LLDB for more background.
Thanks for the reply!
My test was more to see if lldb-dap
is reading and parsing the input it receives via stdin. From my experiment, it appears that lldb-dap
is not reading from stdin (the default behaviour) at all, nor listening on the specified port.
I tried this twice just now. The log files did get created, but were empty (not even a CRLF).
From Emacs’ POV, the error message “Command initialize timed out after 10 seconds” probably means that dape sent the request to stdin but did not receive a response.
Just taking a wild guess here…
I wonder if this could have something to do with \n
vs \r\n
line endings. Maybe \r\n
is converted to \n
during reading from the input stream? Which would then mean that InputStream::read_line
would never find the \r\n
it is waiting for.
There is code which should set stdin/out to binary mode, but we ignore its result in non-assert builds. Maybe those calls failed?
I would probably litter IOStream.cpp
with a lot of *log << "..."
lines to figure out where exactly things are going wrong. Unfortunately, I don’t have access to a Windows machine, so I can’t be of more help here.
I haven’t used lldb-dap.exe with emacs, but I have used it with vscode on Windows 10 and 11, using the “LLDB DAP” extension from the marketplace. I suggest trying that, to see if lldb-dap.exe is working. If that works, then the issue is probably in emacs or the emacs → lldb-dap communication (either emacs not sending things as expected, or lldb-dap not listening correctly).
@tedwoodward Thanks for the suggestion! I tried the VSCode extension and it exhibited the exact same behaviour (frozen initialisation, empty log). The version in question is 19.1.0.
I am trying out with rebuilding and debugging it as per @avogelsgesang’s suggestion.
Debug
builds off current main (818bffcb1c454da8ec778327bde3d974dfe44550
) always worked.
I tried placing the official 19.1.5 release lldb-dap.exe
under debuggers and it demonstrated some interesting behaviour.
There were quite some times I got “Fatal Python error: can’t initialize sys standard streams”.
(6fb8.63e0): Break instruction exception - code 80000003 (first chance)
ntdll!LdrpDoDebuggerBreak+0x35:
00007fff`265e1429 cc int 3
0:000> g
ModLoad: 00007fff`24480000 00007fff`244af000 C:\WINDOWS\System32\IMM32.DLL
ModLoad: 00007fff`22670000 00007fff`2268a000 C:\WINDOWS\SYSTEM32\kernel.appcore.dll
ModLoad: 00007fff`25800000 00007fff`25999000 C:\WINDOWS\System32\ole32.dll
ModLoad: 00007fff`240e0000 00007fff`24179000 C:\WINDOWS\System32\bcryptPrimitives.dll
ModLoad: 00007fff`23030000 00007fff`2304c000 C:\WINDOWS\SYSTEM32\CRYPTSP.dll
ModLoad: 00007fff`225d0000 00007fff`22608000 C:\WINDOWS\system32\rsaenh.dll
ModLoad: 00007fff`23010000 00007fff`2301c000 C:\WINDOWS\SYSTEM32\CRYPTBASE.dll
Fatal Python error: can't initialize sys standard streams
ntdll!NtTerminateProcess+0x14:
00007fff`2661fca4 c3 ret
0:000> !stack
stack is not extension gallery command
No export stack found
0:000> k
# Child-SP RetAddr Call Site
00 00000079`a338e9c8 00007fff`2659fd8e ntdll!NtTerminateProcess+0x14
01 00000079`a338e9d0 00007fff`249018ab ntdll!RtlExitUserProcess+0x11e
02 00000079`a338ea00 00007fff`23af0543 KERNEL32!ExitProcessImplementation+0xb
03 00000079`a338ea30 00007ffe`ae55e272 ucrtbase!common_exit+0xc7
04 00000079`a338ea90 00007ffe`ae55dd8e python310!Py_ExitStatusException+0x52a
05 00000079`a338ead0 00007ffe`ae62d559 python310!Py_ExitStatusException+0x46
06 00000079`a338eb10 00007ffe`7e5af826 python310!Py_gitversion+0x60445
07 00000079`a338ed10 00007ffe`7e5a104a liblldb!PyInit__lldb+0x430646
08 00000079`a338ee30 00007ffe`7e17854f liblldb!PyInit__lldb+0x421e6a
09 00000079`a338ee90 00007ffe`7e32d23e liblldb!lldb::SBUnixSignals::GetSignalAtIndex+0x37f
0a 00000079`a338ef00 00007ffe`7e0c2e43 liblldb!PyInit__lldb+0x1ae05e
0b 00000079`a338ef50 00007ffe`7e0c2d0a liblldb!lldb::SBDebugger::InitializeWithErrorHandling+0x103
0c 00000079`a338f000 00007ff6`c0f0c74f liblldb!lldb::SBDebugger::Initialize+0x9a
0d 00000079`a338f080 00007ff6`c0f5d120 lldb_dap+0x1c74f
0e 00000079`a338f8f0 00007fff`248ee8d7 lldb_dap+0x6d120
0f 00000079`a338f930 00007fff`2659fbcc KERNEL32!BaseThreadInitThunk+0x17
10 00000079`a338f960 00000000`00000000 ntdll!RtlUserThreadStart+0x2c
There were also times it works correctly,
0:008> !uniqstack
Processing 9 threads, please wait
. 0 Id: 5574.26ac Suspend: 1 Teb: 0000006e`d95db000 Unfrozen
Start: lldb_dap+0x6d190 (00007ff6`c0f5d190)
Priority: 0 Priority class: 32 Affinity: ffff
# Child-SP RetAddr Call Site
00 0000006e`d9f8ea58 00007fff`23d6d16d ntdll!NtReadFile+0x14
01 0000006e`d9f8ea60 00007ff6`c0f6add5 KERNELBASE!ReadFile+0x8d
02 0000006e`d9f8ead0 00007ff6`c0f6aade lldb_dap+0x7add5
03 0000006e`d9f8eb70 00007ff6`c0f2c44b lldb_dap+0x7aade
04 0000006e`d9f8ebb0 00007ff6`c0f2c658 lldb_dap+0x3c44b
05 0000006e`d9f8ec30 00007ff6`c0f41293 lldb_dap+0x3c658
06 0000006e`d9f8ecd0 00007ff6`c0f42be4 lldb_dap+0x51293
07 0000006e`d9f8edc0 00007ff6`c0f4374a lldb_dap+0x52be4
08 0000006e`d9f8eed0 00007ff6`c0f0d15a lldb_dap+0x5374a
09 0000006e`d9f8ef60 00007ff6`c0f5d120 lldb_dap+0x1d15a
0a 0000006e`d9f8f7d0 00007fff`248ee8d7 lldb_dap+0x6d120
0b 0000006e`d9f8f810 00007fff`2659fbcc KERNEL32!BaseThreadInitThunk+0x17
0c 0000006e`d9f8f840 00000000`00000000 ntdll!RtlUserThreadStart+0x2c
. 1 Id: 5574.54a8 Suspend: 1 Teb: 0000006e`d95dd000 Unfrozen
Start: ntdll!TppWorkerThread (00007fff`26511a00)
Priority: 0 Priority class: 32 Affinity: ffff
# Child-SP RetAddr Call Site
00 0000006e`da91f488 00007fff`26511d7e ntdll!NtWaitForWorkViaWorkerFactory+0x14
01 0000006e`da91f490 00007fff`248ee8d7 ntdll!TppWorkerThread+0x37e
02 0000006e`da91f7f0 00007fff`2659fbcc KERNEL32!BaseThreadInitThunk+0x17
03 0000006e`da91f820 00000000`00000000 ntdll!RtlUserThreadStart+0x2c
. 4 Id: 5574.7fb8 Suspend: 1 Teb: 0000006e`d95e3000 Unfrozen
Start: lldb_dap+0x75cc8 (00007ff6`c0f65cc8)
Priority: 0 Priority class: 32 Affinity: ffff
# Child-SP RetAddr Call Site
00 0000006e`dc5cf6b8 00007fff`26592e54 ntdll!NtDelayExecution+0x14
01 0000006e`dc5cf6c0 00007fff`23d96661 ntdll!RtlDelayExecution+0x34
02 0000006e`dc5cf6f0 00007ff6`c0f5bd70 KERNELBASE!SleepEx+0x91
03 0000006e`dc5cf780 00007ff6`c0f39d94 lldb_dap+0x6bd70
04 0000006e`dc5cf7d0 00007ff6`c0f65d22 lldb_dap+0x49d94
05 0000006e`dc5cf8a0 00007fff`248ee8d7 lldb_dap+0x75d22
06 0000006e`dc5cf8d0 00007fff`2659fbcc KERNEL32!BaseThreadInitThunk+0x17
07 0000006e`dc5cf900 00000000`00000000 ntdll!RtlUserThreadStart+0x2c
. 5 Id: 5574.7028 Suspend: 1 Teb: 0000006e`d95e5000 Unfrozen
Start: lldb_dap+0x75cc8 (00007ff6`c0f65cc8)
Priority: 0 Priority class: 32 Affinity: ffff
# Child-SP RetAddr Call Site
00 0000006e`dcf5e688 00007fff`23d6d16d ntdll!NtReadFile+0x14
01 0000006e`dcf5e690 00007ff6`c0f6add5 KERNELBASE!ReadFile+0x8d
02 0000006e`dcf5e700 00007ff6`c0f6aade lldb_dap+0x7add5
03 0000006e`dcf5e7a0 00007ff6`c0f381ff lldb_dap+0x7aade
04 0000006e`dcf5e7e0 00007ff6`c0f65d22 lldb_dap+0x481ff
05 0000006e`dcf5f840 00007fff`248ee8d7 lldb_dap+0x75d22
06 0000006e`dcf5f870 00007fff`2659fbcc KERNEL32!BaseThreadInitThunk+0x17
07 0000006e`dcf5f8a0 00000000`00000000 ntdll!RtlUserThreadStart+0x2c
. 7 Id: 5574.27f4 Suspend: 1 Teb: 0000006e`d95e9000 Unfrozen "lldb.debugger.alarm-thread"
Start: liblldb!PyInit__lldb+0x196100c (00007ffe`7fae01ec)
Priority: 0 Priority class: 32 Affinity: ffff
# Child-SP RetAddr Call Site
00 0000006e`de27f4f8 00007fff`2651f574 ntdll!NtWaitForAlertByThreadId+0x14
01 0000006e`de27f500 00007fff`23d6d8f8 ntdll!RtlSleepConditionVariableSRW+0x1c4
02 0000006e`de27f5a0 00007ffe`7fac1341 KERNELBASE!SleepConditionVariableSRW+0x38
03 0000006e`de27f5e0 00007ffe`7fac119a liblldb!PyInit__lldb+0x1942161
04 0000006e`de27f610 00007ffe`7e7272e9 liblldb!PyInit__lldb+0x1941fba
05 0000006e`de27f640 00007ffe`7e727abd liblldb!PyInit__lldb+0x5a8109
06 0000006e`de27f730 00007ffe`7ecd7f36 liblldb!PyInit__lldb+0x5a88dd
07 0000006e`de27f760 00007ffe`7fae0246 liblldb!PyInit__lldb+0xb58d56
08 0000006e`de27f7f0 00007fff`248ee8d7 liblldb!PyInit__lldb+0x1961066
09 0000006e`de27f820 00007fff`2659fbcc KERNEL32!BaseThreadInitThunk+0x17
0a 0000006e`de27f850 00000000`00000000 ntdll!RtlUserThreadStart+0x2c
. 8 Id: 5574.4784 Suspend: 1 Teb: 0000006e`d95eb000 Unfrozen
Start: ntdll!DbgUiRemoteBreakin (00007fff`265f51b0)
Priority: 0 Priority class: 32 Affinity: ffff
# Child-SP RetAddr Call Site
00 0000006e`dec0fa48 00007fff`265f51fe ntdll!DbgBreakPoint
01 0000006e`dec0fa50 00007fff`248ee8d7 ntdll!DbgUiRemoteBreakin+0x4e
02 0000006e`dec0fa80 00007fff`2659fbcc KERNEL32!BaseThreadInitThunk+0x17
03 0000006e`dec0fab0 00000000`00000000 ntdll!RtlUserThreadStart+0x2c
Total threads: 9
Duplicate callstacks: 3 (windbg thread #s follow):
2, 3, 6
Most of the times is the common behaviour of not registering keys but also not erroring out,
(83cc.37e4): Break instruction exception - code 80000003 (first chance)
ntdll!DbgBreakPoint:
00007fff`265e3140 cc int 3
0:008> !uniqstack
Processing 9 threads, please wait
. 0 Id: 83cc.70c Suspend: 1 Teb: 0000001d`3b9cd000 Unfrozen
Start: lldb_dap+0x6d190 (00007ff6`c0f5d190)
Priority: 0 Priority class: 32 Affinity: ffff
# Child-SP RetAddr Call Site
00 0000001d`3c38e318 00007fff`23de47cc ntdll!NtQueryInformationFile+0x14
01 0000001d`3c38e320 00007fff`23a6ef16 KERNELBASE!SetFilePointerEx+0x6c
02 0000001d`3c38e3b0 00007fff`23a6e281 ucrtbase_7fff23a50000!common_lseek<__int64>+0xc6
03 0000001d`3c38e420 00007ffe`ae473db0 ucrtbase_7fff23a50000!lseeki64+0x41
04 0000001d`3c38e490 00007ffe`ae473d47 python310!PyType_GetModuleByDef+0x3a8
05 0000001d`3c38e4d0 00007ffe`ae4ac70f python310!PyType_GetModuleByDef+0x33f
06 0000001d`3c38e500 00007ffe`ae4ac310 python310!PyObject_GetMethod+0x383
07 0000001d`3c38e550 00007ffe`ae474558 python310!PyObject_VectorcallMethod+0x88
08 0000001d`3c38e5a0 00007ffe`ae4744f5 python310!Py_get_osfhandle_noraise+0x400
09 0000001d`3c38e5d0 00007ffe`ae59fe25 python310!Py_get_osfhandle_noraise+0x39d
0a 0000001d`3c38e600 00007ffe`ae59fd61 python310!PyArg_NoPositional+0x235d
0b 0000001d`3c38e630 00007ffe`ae4bff84 python310!PyArg_NoPositional+0x2299
0c 0000001d`3c38e6a0 00007ffe`ae4aeadb python310!PyType_GenericNew+0x11f4
0d 0000001d`3c38e6d0 00007ffe`ae4e0b57 python310!PyObject_SetAttr+0x8c3
0e 0000001d`3c38e720 00007ffe`ae4e0ac1 python310!PyObject_CallFunction_SizeT+0xc7
0f 0000001d`3c38e7c0 00007ffe`ae4e0813 python310!PyObject_CallFunction_SizeT+0x31
10 0000001d`3c38e800 00007ffe`ae4dee1c python310!PySequence_List+0x42b
11 0000001d`3c38e8d0 00007ffe`ae484681 python310!Py_GetConfig+0x130
12 0000001d`3c38e9f0 00007ffe`ae4ae9cd python310!PyArg_CheckPositional+0x4d1
13 0000001d`3c38ea30 00007ffe`ae4e0b57 python310!PyObject_SetAttr+0x7b5
14 0000001d`3c38ea80 00007ffe`ae4e0a79 python310!PyObject_CallFunction_SizeT+0xc7
15 0000001d`3c38eb20 00007ffe`ae4d7a68 python310!PySequence_List+0x691
16 0000001d`3c38eb60 00007ffe`ae4d77bd python310!PyObject_CallMethodId+0x48
17 0000001d`3c38ebb0 00007ffe`ae51ffef python310!PyType_LookupId+0x135
18 0000001d`3c38ec60 00007ffe`ae50c850 python310!PyModule_AddIntConstant+0x15c3
19 0000001d`3c38ecd0 00007ffe`ae53a250 python310!PySequence_Size+0xc80
1a 0000001d`3c38ed50 00007ffe`ae539273 python310!PyInterpreterState_Enable+0x374
1b 0000001d`3c38edc0 00007ffe`ae5bb455 python310!Py_InitializeFromConfig+0x73
1c 0000001d`3c38ee30 00007ffe`7e5af826 python310!Py_InitializeEx+0x59
1d 0000001d`3c38f030 00007ffe`7e5a104a liblldb!PyInit__lldb+0x430646
1e 0000001d`3c38f150 00007ffe`7e17854f liblldb!PyInit__lldb+0x421e6a
1f 0000001d`3c38f1b0 00007ffe`7e32d23e liblldb!lldb::SBUnixSignals::GetSignalAtIndex+0x37f
20 0000001d`3c38f220 00007ffe`7e0c2e43 liblldb!PyInit__lldb+0x1ae05e
21 0000001d`3c38f270 00007ffe`7e0c2d0a liblldb!lldb::SBDebugger::InitializeWithErrorHandling+0x103
22 0000001d`3c38f320 00007ff6`c0f0c74f liblldb!lldb::SBDebugger::Initialize+0x9a
23 0000001d`3c38f3a0 00007ff6`c0f5d120 lldb_dap+0x1c74f
24 0000001d`3c38fc10 00007fff`248ee8d7 lldb_dap+0x6d120
25 0000001d`3c38fc50 00007fff`2659fbcc KERNEL32!BaseThreadInitThunk+0x17
26 0000001d`3c38fc80 00000000`00000000 ntdll!RtlUserThreadStart+0x2c
. 1 Id: 83cc.5b64 Suspend: 1 Teb: 0000001d`3b9cf000 Unfrozen
Start: ntdll!TppWorkerThread (00007fff`26511a00)
Priority: 0 Priority class: 32 Affinity: ffff
# Child-SP RetAddr Call Site
00 0000001d`3cd1fb98 00007fff`26511d7e ntdll!NtWaitForWorkViaWorkerFactory+0x14
01 0000001d`3cd1fba0 00007fff`248ee8d7 ntdll!TppWorkerThread+0x37e
02 0000001d`3cd1ff00 00007fff`2659fbcc KERNEL32!BaseThreadInitThunk+0x17
03 0000001d`3cd1ff30 00000000`00000000 ntdll!RtlUserThreadStart+0x2c
. 4 Id: 83cc.2780 Suspend: 1 Teb: 0000001d`3b9d5000 Unfrozen
Start: lldb_dap+0x75cc8 (00007ff6`c0f65cc8)
Priority: 0 Priority class: 32 Affinity: ffff
# Child-SP RetAddr Call Site
00 0000001d`3e9cf5f8 00007fff`26592e54 ntdll!NtDelayExecution+0x14
01 0000001d`3e9cf600 00007fff`23d96661 ntdll!RtlDelayExecution+0x34
02 0000001d`3e9cf630 00007ff6`c0f5bd70 KERNELBASE!SleepEx+0x91
03 0000001d`3e9cf6c0 00007ff6`c0f39d94 lldb_dap+0x6bd70
04 0000001d`3e9cf710 00007ff6`c0f65d22 lldb_dap+0x49d94
05 0000001d`3e9cf7e0 00007fff`248ee8d7 lldb_dap+0x75d22
06 0000001d`3e9cf810 00007fff`2659fbcc KERNEL32!BaseThreadInitThunk+0x17
07 0000001d`3e9cf840 00000000`00000000 ntdll!RtlUserThreadStart+0x2c
. 5 Id: 83cc.5458 Suspend: 1 Teb: 0000001d`3b9d7000 Unfrozen
Start: lldb_dap+0x75cc8 (00007ff6`c0f65cc8)
Priority: 0 Priority class: 32 Affinity: ffff
# Child-SP RetAddr Call Site
00 0000001d`3f35e9d8 00007fff`23d6d16d ntdll!NtReadFile+0x14
01 0000001d`3f35e9e0 00007ff6`c0f6add5 KERNELBASE!ReadFile+0x8d
02 0000001d`3f35ea50 00007ff6`c0f6aade lldb_dap+0x7add5
03 0000001d`3f35eaf0 00007ff6`c0f381ff lldb_dap+0x7aade
04 0000001d`3f35eb30 00007ff6`c0f65d22 lldb_dap+0x481ff
05 0000001d`3f35fb90 00007fff`248ee8d7 lldb_dap+0x75d22
06 0000001d`3f35fbc0 00007fff`2659fbcc KERNEL32!BaseThreadInitThunk+0x17
07 0000001d`3f35fbf0 00000000`00000000 ntdll!RtlUserThreadStart+0x2c
. 7 Id: 83cc.6614 Suspend: 1 Teb: 0000001d`3b9db000 Unfrozen "lldb.debugger.alarm-thread"
Start: liblldb!PyInit__lldb+0x196100c (00007ffe`7fae01ec)
Priority: 0 Priority class: 32 Affinity: ffff
# Child-SP RetAddr Call Site
00 0000001d`4067fb58 00007fff`2651f574 ntdll!NtWaitForAlertByThreadId+0x14
01 0000001d`4067fb60 00007fff`23d6d8f8 ntdll!RtlSleepConditionVariableSRW+0x1c4
02 0000001d`4067fc00 00007ffe`7fac1341 KERNELBASE!SleepConditionVariableSRW+0x38
03 0000001d`4067fc40 00007ffe`7fac119a liblldb!PyInit__lldb+0x1942161
04 0000001d`4067fc70 00007ffe`7e7272e9 liblldb!PyInit__lldb+0x1941fba
05 0000001d`4067fca0 00007ffe`7e727abd liblldb!PyInit__lldb+0x5a8109
06 0000001d`4067fd90 00007ffe`7ecd7f36 liblldb!PyInit__lldb+0x5a88dd
07 0000001d`4067fdc0 00007ffe`7fae0246 liblldb!PyInit__lldb+0xb58d56
08 0000001d`4067fe50 00007fff`248ee8d7 liblldb!PyInit__lldb+0x1961066
09 0000001d`4067fe80 00007fff`2659fbcc KERNEL32!BaseThreadInitThunk+0x17
0a 0000001d`4067feb0 00000000`00000000 ntdll!RtlUserThreadStart+0x2c
. 8 Id: 83cc.37e4 Suspend: 1 Teb: 0000001d`3b9dd000 Unfrozen
Start: ntdll!DbgUiRemoteBreakin (00007fff`265f51b0)
Priority: 0 Priority class: 32 Affinity: ffff
# Child-SP RetAddr Call Site
00 0000001d`4100fb28 00007fff`265f51fe ntdll!DbgBreakPoint
01 0000001d`4100fb30 00007fff`248ee8d7 ntdll!DbgUiRemoteBreakin+0x4e
02 0000001d`4100fb60 00007fff`2659fbcc KERNEL32!BaseThreadInitThunk+0x17
03 0000001d`4100fb90 00000000`00000000 ntdll!RtlUserThreadStart+0x2c
Total threads: 9
Duplicate callstacks: 3 (windbg thread #s follow):
2, 3, 6
I wonder if it is some race condition between Python init_sys_streams
and lldb-dap _setmode
call inside DAP
constructor…
I cannot reproduce the issue on local RelWithDbgInfo builds, but a crude bisection on official releases indicates that the breakage was introduced between 18.1.8 (19 Jun) and 19.1.0 (17 Sep).
CC @ashgti for possible interest.