Invoke `cdb.exe` (windbg) to run noninteractively, and produce backtrace in case of crash?
Asked Answered
B

2

6

I have a C++ project which I use with travis-ci. Right now, I build it using boost.build, and on travis, when I run the unit tests, I do it through gdb, so that I get a backtrace in the event of a crash.

To make gdb do this noninteractively, I invoke it like this on command-line:

gdb -return-child-result -batch -ex "run" -ex "thread apply all bt" -ex "quit" --args ./${file}

where ${file} is my executable.

This tells it to:

  • start the process
  • apply bt to all threads, which emits a backtrace in event of crash and also does nothing if there was no crash.
  • finally it causes gdb to quit, and exit with the child's exit code.

Now I would like to do the same thing on appveyor.

Boost build seems to work perfectly out of the box in the appveyor VM, so hats off to them.

However, I'm struggling to figure out how to configure cdb, the console cousin of windbg. It seems to hang in my build logs. Most of the examples I find online have to do with inspecting minidump files, not starting a process and debugging it as it runs.

I'm currently invoking cdb like this (from appveyor powershell script):

cdb -c "$$><cdb_script.txt" -o $file.fullName

And my cdb_script.txt looks like

.sympath srv*C:\Windows\Symbols*http://msdl.microsoft.com/download/symbols;
.reload;
~* k 99;
q

I'm basically cobbling this together from various things I googled, including

I really hoped to find better docu or examples about how to do this specifically.

The error I get right now is:

Microsoft (R) Windows Debugger Version 6.2.9200.20512 X86
Copyright (c) Microsoft Corporation. All rights reserved.
CommandLine: C:\projects\primer\test\stage\api.exe
Symbol search path is: *** Invalid ***
****************************************************************************
* Symbol loading may be unreliable without a symbol search path.           *
* Use .symfix to have the debugger choose a symbol path.                   *
* After setting your symbol path, use .reload to refresh symbol locations. *
****************************************************************************
Executable search path is: 
ModLoad: 009a0000 00a27000   api.exe 
ModLoad: 76fb0000 7711f000   ntdll.dll
ModLoad: 76520000 76660000   C:\windows\SysWOW64\KERNEL32.DLL
ModLoad: 75fb0000 76087000   C:\windows\SysWOW64\KERNELBASE.dll
ModLoad: 74350000 743f0000   C:\windows\SysWOW64\apphelp.dll
SHIMVIEW: ShimInfo(Complete)
ModLoad: 73b20000 73bd9000   C:\windows\SysWOW64\MSVCP140D.dll
ModLoad: 741b0000 741cc000   C:\windows\SysWOW64\VCRUNTIME140D.dll
ModLoad: 71bd0000 71d46000   C:\windows\SysWOW64\ucrtbased.dll
(294.49c): Break instruction exception - code 80000003 (first chance)
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for ntdll.dll - 
eax=00000000 ebx=00000000 ecx=19a00000 edx=00000000 esi=7ecdf000 edi=00000000
eip=77063c7d esp=0110f8d4 ebp=0110f900 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
ntdll!LdrInitShimEngineDynamic+0x6dd:
77063c7d cc              int     3
0:000> cdb: Reading initial command '><cdb_script.txt'
       ^ Syntax error in '><cdb_script.txt'

Some variations I tried:

  • Using $$<cdb_script.txt instead of $$><cdb_script.txt.
  • Putting a semicolon after the last command in the script file

Edit: I found also this answer, which explains again how to do it with minidumps but shows the script file in more detail.

I don't really know what a minidump is tbh so -o option sounds more attractive, at least, more like gdb. But maybe I will end up trying to do it with minidumps if I can't figure this out.

Brisco answered 17/11, 2016 at 7:17 Comment(0)
D
2

Powershell variables use $ like in $file.fullName. This also seems to destroy the $$ in $$><.

You can escape that by a backtick, like

`$`$><cdb_script.txt

The following worked for me:

PS C:\Users\thomas.weller> & "C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x86\cdb.exe" -c "`$`$><c:\cdb_script.txt" -o notepad.exe
[...]
0:000> cdb: Reading initial command '$$><c:\cdb_script.txt'
Yay!
0:000>
Decorative answered 17/11, 2016 at 10:31 Comment(1)
I ended up not using the cdb script, it turns out my script is really not that long, but thanks for this pointerBrisco
B
2

What I ultimately got to work was this:

cdb -y $SymPath -c "|;g;kv;q" -o $file.fullName

using -y to set the symbol path from the command line, and I am setting SymPath in my powershell script to be a simple path, where (I think) I am copying the pdb files:

$SymPath = "C:\projects\primer\test\stage"

(I think that part is not actually working, I still get some complaints about lack of symbols but the backtraces nevertheless seem correct...)

The cdb commands are documented under windbg on the ms page here it turns out:

  • | means "list the active processes", it is not necessary but gives some info for the log
  • g means "go", it is similar to start in gdb I think
  • kv means "verbose backtrace", it is similar to bt in gdb I think.
  • q means "quit", similarly to gdb

I'm still not sure how to get the child exit code, I might post that as a separate question...

Brisco answered 17/11, 2016 at 11:18 Comment(3)
I'm not sure whether that's a very reliable way of analyzing crashes. Please have a look at the LocalDumps Registry key, capture a crash dump when your process crashes and you'll have all a snapshot of everything you need in order to analyze. Just think of a stack corruption in which case "kv" is useless. You'll have nothing with your script, but having a crash dump you can still do some analysis. Same for the symbols: given you forgot to copy them beforehand, you'll have nothing but numbers. With a crash dump you can still fix the symbols.Decorative
Thanks for the tips, I will try to read up. This is only for a small project right now, but I didn't realize that kv is useless in case of a stack corruption. Maybe bt is also useless there with gdb... ?Brisco
Actually g seems to be closer to continue (AKA c) in GDB, since the CDB command line is interpreted after getting to int3 in LdrpDoDebuggerBreak(), at which point the program is already running.Taneshatang

© 2022 - 2024 — McMap. All rights reserved.