Gdb scripting for core dump analysis - c++

I have a core dump for c/c++ application. I am new to programming so this question may sound silly. Is there some how I can write scripts to use gdb to analyze the core dump?

Yes, just script whatever you want. For example:
gdb \
-ex "set pagination 0" \
-ex "thread apply all bt" \
-batch ${EXECUTABLE_FILE} ${CORE_FILE}

Use -x option as below
$ cat gdb.cmds
set confirm off
set height 0
cd /homes/syrajendra
file dump
core dump.core
set solib-search-path "/lib"
bt
printf "\n"
quit
$ gdb -x gdb.cmds
. . .
[Thread debugging using libthread_db enabled]
Core was generated by `dump'.
Program terminated with signal SIGABRT, Aborted.
#0 0x000000080149f6ca in thr_kill () from /lib/libc.so.7
#0 0x000000080149f6ca in thr_kill () from /lib/libc.so.7
#1 0x0000000801574149 in abort () from /lib/libc.so.7
#2 0x0000000801556011 in __assert () from /lib/libc.so.7
#3 0x000000000040130a in fun2 (num=100) at ./dump.cpp:10
#4 0x0000000000401343 in fun1 (num=100) at ./dump.cpp:20
#5 0x000000000040137e in main () at ./dump.cpp:27

Related

Address sanitizer: PC is at a non-executable region. Maybe a wild jump?

My compiled code is faulty and I decided to use address sanitizer to find its problem with g++ and options -Og -g3 -fsanitize=address -fno-omit-frame-pointer.
I used llvm-symbolizer
export ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer-9
$ ASAN_OPTIONS=symbolize=1 ./a.out
AddressSanitizer:DEADLYSIGNAL
=================================================================
==7296==ERROR: AddressSanitizer: SEGV on unknown address 0x000200000039 (pc 0x000200000039 bp 0x000000000001 sp 0x7ffd2b566c48 T0)
==7296==The signal is caused by a READ memory access.
==7296==Hint: PC is at a non-executable region. Maybe a wild jump?
AddressSanitizer:DEADLYSIGNAL
AddressSanitizer: nested bug in the same thread, aborting.
It has no meaningful information that helps me finding the source of the problem in my code
It does not dump the stack trace. What does it mean and how can I find the stack-trace or the location of the dumped addresses?
update: Using gdb does not give much more info too
(gdb) run
Starting program: /media/bin/a.out
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Full compilation was performed in 0.005376 seconds.
Code has an error.
No input file is given
==8965==LeakSanitizer has encountered a fatal error.
==8965==HINT: For debugging, try setting environment variable LSAN_OPTIONS=verbosity=1:log_threads=1
==8965==HINT: LeakSanitizer does not work under ptrace (strace, gdb, etc)
Program received signal SIGABRT, Aborted.
0x00007ffff1eac438 in __GI_raise (sig=sig#entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
54 ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0 0x00007ffff1eac438 in __GI_raise (sig=sig#entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1 0x00007ffff1eae03a in __GI_abort () at abort.c:89
#2 0x00007ffff72d14be in ?? () from /usr/lib/x86_64-linux-gnu/libasan.so.5
#3 0x00007ffff72dbe78 in ?? () from /usr/lib/x86_64-linux-gnu/libasan.so.5
#4 0x00007ffff72e0ecf in ?? () from /usr/lib/x86_64-linux-gnu/libasan.so.5
#5 0x00007ffff72e0f05 in ?? () from /usr/lib/x86_64-linux-gnu/libasan.so.5
#6 0x00007ffff1eb137a in __cxa_finalize (d=0x7ffff752e4a0) at cxa_finalize.c:56
#7 0x00007ffff71ca793 in ?? () from /usr/lib/x86_64-linux-gnu/libasan.so.5
#8 0x00007fffffffdc50 in ?? ()
#9 0x00007ffff7de7e27 in _dl_fini () at dl-fini.c:235
Backtrace stopped: frame did not save the PC

GDB shows more number of backtraces (90) for my core file

GDB 7.7 shows more number of backtraces (90) for my core file. It is problem with GDB or core file or stack corruption issue?
(gdb) bt
Python Exception exceptions.ImportError No module named traceback:
#0 0x00007f422fd04c37 in ?? () from /users/jegan/lib/x86_64-linux-gnu/libc.so.6
#1 0x0000000000002929 in ?? ()
#2 0x7328203c20746e63 in ?? ()
.......................
......................
#88 0x544143494649544e in ?? ()
#89 0x29295d305b4e4f49 in ?? ()
#90 0x0000000000000000 in ?? ()
(gdb)
GDB 7.7 shows more number of backtraces (90) for my core file.
There are a few likely causes for this:
You didn't invoke GDB correctly, or
You are analysing a core dump on a different host from the one it was produced on (or the same host has had its system libraries updated).
Answer for #1.
Answer for #2.

Calling a shared library from Haskell via FFI blocks, while it doesn't when linked from a C program

I'm trying to interface with a Basler USB3 camera from a Haskell application, but I'm experiencing some difficulty. The camera comes with a C++ library that makes it fairly straight forward. The following code can be used to acquire a camera source:
extern "C" {
void basler_init() {
PylonAutoInitTerm pylon;
CInstantCamera camera( CTlFactory::GetInstance().CreateFirstDevice());
camera.RegisterConfiguration( (CConfigurationEventHandler*) NULL, RegistrationMode_ReplaceAll, Cleanup_None);
cout << "Using device " << camera.GetDeviceInfo().GetModelName() << endl;
}
}
I've used this source code to build a shared library - libbasler.so. To confirm it works, here's a basic C program that links against it and confirms everything is working:
void basler_init();
int main () {
basler_init();
}
I compile and run this as:
$ gcc Test2.c -lbasler -Llib -Wl,--enable-new-dtags -Wl,-rpath,pylon5/lib64 -Wl,-E -lpylonbase -o Test2-c
$ PYLON_CAMEMU=1 LD_LIBRARY_PATH=lib ./Test2-c
Using device Emulation
This is the expected output.
However, when I try and use this with Haskell, the behaviour changes and the program blocks indefinitely. Here's the Haskell source code:
{-# LANGUAGE ForeignFunctionInterface #-}
foreign import ccall "basler_init" baslerInit :: IO ()
main :: IO ()
main = baslerInit
I compile and run this as:
$ ghc --make Test2.hs -o Test2-haskell -Llib -lbasler -optl-Wl,--enable-new-dtags -optl-Wl,-rpath,pylon5/lib64 -optl-Wl,-E -lpylonbase
$ PYLON_CAMEMU=1 LD_LIBRARY_PATH=lib ./Test2-haskell
The application now hangs indefinitely.
I have ran both through strace to try and get an idea of what is going on, but I'm unable to really make much sense of it. The output is too long to add here, but please see these two pastes:
strace output for the C application: https://gist.github.com/ocharles/001b5f42c09229bc7a8482a22cadf486
strace output for the Haskell application: https://gist.github.com/ocharles/4c1c45a9ee78f75cd723f1a2910998f3
On top of that, I've used gdb to try and ascertain where the Haskell application is getting stuck:
$ PYLON_CAMEMU=1 LD_LIBRARY_PATH=lib gdb Test2-haskell
GNU gdb (GDB) 7.11
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from Test2-haskell...done.
(gdb) run
Starting program: /home/ollie/work/circuithub/receiving-station/Test2-haskell
warning: File "/nix/store/9ljgbhb26ca0j9shwh8bwsa77h42izr2-gcc-5.4.0-lib/lib/libstdc++.so.6.0.21-gdb.py" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
add-auto-load-safe-path /nix/store/9ljgbhb26ca0j9shwh8bwsa77h42izr2-gcc-5.4.0-lib/lib/libstdc++.so.6.0.21-gdb.py
line to your configuration file "/home/ollie/.gdbinit".
To completely disable this security protection add
set auto-load safe-path /
line to your configuration file "/home/ollie/.gdbinit".
For more information about this security protection see the
"Auto-loading safe path" section in the GDB manual. E.g., run from the shell:
info "(gdb)Auto-loading safe path"
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/nix/store/bb32xf954imhdrzn7j8h82xs1bx7p3fr-glibc-2.23/lib/libthread_db.so.1".
^C
Program received signal SIGINT, Interrupt.
0x00007ffff6c6fb33 in __recvfrom_nocancel () from /nix/store/98s2znxww6x7h2ch7cj1w5givahxmdna-glibc-2.23/lib/libc.so.6
(gdb) bt
#0 0x00007ffff6c6fb33 in __recvfrom_nocancel () from /nix/store/98s2znxww6x7h2ch7cj1w5givahxmdna-glibc-2.23/lib/libc.so.6
#1 0x00007fffedb885c2 in GxImp::CEnumCollector::OnReady(unsigned int, _GX_SOCKET_INTERFACE_INFO const*) () from /home/ollie/work/circuithub/receiving-station/pylon5/lib64/libgxapi-5.0.1.so
#2 0x00007fffedb8d54d in CCollector::Collect(GxImp::CSocket*, unsigned int, unsigned int, _GX_SOCKET_INTERFACE_INFO const*) () from /home/ollie/work/circuithub/receiving-station/pylon5/lib64/libgxapi-5.0.1.so
#3 0x00007fffedb8817b in CBroadcastSocketCollection::Collect(CCollector&, unsigned int) () from /home/ollie/work/circuithub/receiving-station/pylon5/lib64/libgxapi-5.0.1.so
#4 0x00007fffedb889ab in Gx::Enumerator::Discover(Gx::Enumerator::Callee*, unsigned int, unsigned int, sockaddr const*) () from /home/ollie/work/circuithub/receiving-station/pylon5/lib64/libgxapi-5.0.1.so
#5 0x00007fffeddeaca0 in Pylon::CBaslerGigETl::DoDeviceEnumeration(Pylon::DeviceInfoList&, bool, sockaddr const*) () from pylon5/lib64/libpylon_TL_gige-5.0.1.so
#6 0x00007fffeddeaebc in Pylon::CBaslerGigETl::InternalEnumerateDevices(Pylon::DeviceInfoList&) () from pylon5/lib64/libpylon_TL_gige-5.0.1.so
#7 0x00007fffeddf3c99 in Pylon::CTransportLayerBase<Pylon::IGigETransportLayer>::EnumerateDevices(Pylon::DeviceInfoList&, Pylon::DeviceInfoList const&, bool) () from pylon5/lib64/libpylon_TL_gige-5.0.1.so
#8 0x00007ffff7949669 in Pylon::CTlFactory::EnumerateDevices(Pylon::DeviceInfoList&, Pylon::DeviceInfoList const&, bool) () from pylon5/lib64/libpylonbase-5.0.1.so
#9 0x00007ffff7949c8f in Pylon::CTlFactory::InternalCreateDevice(Pylon::CDeviceInfo const&, GenICam_3_0_Basler_pylon_v5_0::gcstring_vector const&, bool) () from pylon5/lib64/libpylonbase-5.0.1.so
#10 0x00007ffff794a655 in Pylon::CTlFactory::CreateFirstDevice(Pylon::CDeviceInfo const&) () from pylon5/lib64/libpylonbase-5.0.1.so
#11 0x00007ffff7bd7dc5 in basler_init () from lib/libbasler.so
#12 0x0000000000438415 in rFl_info ()
#13 0x0000000000000000 in ?? ()
For the C program:
Reading symbols from Test2-c...done.
(gdb) run
Starting program: /home/ollie/work/circuithub/receiving-station/Test2-c
warning: File "/nix/store/9ljgbhb26ca0j9shwh8bwsa77h42izr2-gcc-5.4.0-lib/lib/libstdc++.so.6.0.21-gdb.py" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
add-auto-load-safe-path /nix/store/9ljgbhb26ca0j9shwh8bwsa77h42izr2-gcc-5.4.0-lib/lib/libstdc++.so.6.0.21-gdb.py
line to your configuration file "/home/ollie/.gdbinit".
To completely disable this security protection add
set auto-load safe-path /
line to your configuration file "/home/ollie/.gdbinit".
For more information about this security protection see the
"Auto-loading safe path" section in the GDB manual. E.g., run from the shell:
info "(gdb)Auto-loading safe path"
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/nix/store/bb32xf954imhdrzn7j8h82xs1bx7p3fr-glibc-2.23/lib/libthread_db.so.1".
[New Thread 0x7fffed4ae700 (LWP 13792)]
[New Thread 0x7fffeccad700 (LWP 13793)]
Using device Emulation
[Thread 0x7fffeccad700 (LWP 13793) exited]
[Thread 0x7fffed4ae700 (LWP 13792) exited]
[Inferior 1 (process 13788) exited normally]
My guess is GHC's runtime is doing something that causes pthreads to have different behaviour, but I'm not sure what that could be.
I believe this TRAC commentary is relevant:
https://ghc.haskell.org/trac/ghc/wiki/Commentary/Rts/Signals
The difference occurs starting a line 437 in the C strace output
vs. line 495 in the Haskell strace output.
At this point the library creates two UDP sockets and sends two UDP datagrams
(lines 448-449 C / lines 506-507 Haskell). The packets are broadcast to
two local networks: 192.168.1.0/24 and 192.168.56.0/24.
It then waits for a response on either of these sockets with a timeout of (apparently)
25 microseconds (line 450 C / line 508 Haskell). In the C case the select call
times out. In the Haskell case the select call is repeatedly interrupted by
a SIGVTALRM signal which is used by the GHC RTS. This is the same pattern
which is shown in the the above TRAC commentary.
For a potential fix, have a look at how the mysql package implements and uses the block_rts_signals() and unblock_rts_signals() macros:
mysql_signals.c

How do I debug a failing cargo test in GDB?

I have a failing Cargo test:
$ cargo test
[snip]
Running target/gunzip-c62d8688496249d8
running 2 tests
test test_extract_failure ... FAILED
test test_extract_success ... ok
failures:
---- test_extract_failure stdout ----
task 'test_extract_failure' panicked at 'assertion failed: result.is_err()', /home/dhardy/other/flate2-rs/tests/gunzip.rs:19
failures:
test_extract_failure
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured
task '<main>' panicked at 'Some tests failed', /home/rustbuild/src/rust-buildbot/slave/nightly-linux/build/src/libtest/lib.rs:250
How do I launch the failing test in a debugger like GDB?
This should be a general question, but for those wanting to retrace my steps, install a recent nightly Rust build and:
git clone https://github.com/dhardy/flate2-rs.git
git checkout 24979640a880
cd flate2-rs
cargo test
You can get a test binary to filter the tests it runs by passing additional arguments to it; Cargo exposes this directly, too. Thus, cargo test test_extract_failure will just run that specific case. (This is convenient if you have other tests that panic and are expected to fail, so that they won’t call the rust_panic function I am about to mention, leaving only the offending call there.)
In order to use gdb, you’ll need to run the test binary directly (if you use Cargo it runs in a subprocess and thus gdb won’t catch panics inside it). Cargo helpfully tells you the file name, target/gunzip-c62d8688496249d8. You can run this directly with --test to make it a test run:
$ target/gunzip-c62d8688496249d8 --test test_extract_failure
running 1 test
test test_extract_failure ... FAILED
failures:
---- test_extract_failure stdout ----
task 'test_extract_failure' panicked at 'assertion failed: result.is_err()', /home/dhardy/other/flate2-rs/tests/gunzip.rs:19
failures:
test_extract_failure
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured
task '<main>' panicked at 'Some tests failed', /home/rustbuild/src/rust-buildbot/slave/nightly-linux/build/src/libtest/lib.rs:250
Now to hook it up with gdb. There is a convenient function for which you can insert a breakpoint, rust_panic. Once in gdb, break rust_panic means it will pause whenever something triggers a panic, before actually doing the unwinding.
Here’s what a session might end up looking like:
$ gdb target/demo-92d91e26f6ebc557
…
Reading symbols from target/demo-92d91e26f6ebc557...done.
(gdb) break rust_panic
Breakpoint 1 at 0xccb60
(gdb) run --test test_extract_failure
Starting program: /tmp/demo/target/demo-92d91e26f6ebc557 --test test_extract_failure
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
running 1 test
[New Thread 0x7ffff6ef4700 (LWP 14254)]
[New Thread 0x7ffff5fff700 (LWP 14255)]
[Switching to Thread 0x7ffff5fff700 (LWP 14255)]
Breakpoint 1, 0x0000555555620b60 in rust_panic ()
(gdb) bt
#0 0x0000555555620b60 in rust_panic ()
#1 0x0000555555621274 in unwind::begin_unwind_inner::hb821324209c8ed246Qc ()
#2 0x000055555556bb6d in unwind::begin_unwind::h7834652822578025936 ()
#3 0x000055555556b9fd in demo::do_something () at <std macros>:8
#4 0x000055555556b98e in demo::test_extract_failure () at src/lib.rs:3
#5 0x000055555559aa4b in task::TaskBuilder::try_future::closure.8077 ()
#6 0x000055555560fd03 in task::TaskBuilder::spawn_internal::closure.30919 ()
#7 0x000055555561f672 in task::Task::spawn::closure.5759 ()
#8 0x0000555555621cac in rust_try_inner ()
#9 0x0000555555621c96 in rust_try ()
#10 0x000055555561f713 in unwind::try::ha8078a6ae9b50ccepFc ()
#11 0x000055555561f51c in task::Task::run::hdb5fabf381084abafOb ()
#12 0x000055555561f168 in task::Task::spawn::closure.5735 ()
#13 0x0000555555620595 in thread::thread_start::h4d73784c295273b3i6b ()
#14 0x00007ffff79c2314 in start_thread () from /usr/lib/libpthread.so.0
#15 0x00007ffff72e25bd in clone () from /usr/lib/libc.so.6
(gdb)
In that particular case, #0–#2 and #5–#15 are noise, #3 and #4 are the signal we want.
cargo build now has a parameter
--test [<NAME>] Build only the specified test target
which builds a binary with only the set of tests provided as a parameter.

Point cloud library apps difficult to debug, possibly due to threading?

I'm using the Point Cloud Library with cmake for compilation, and I've got it building in debug mode, but my program doesn't seg fault or abort in the way I'd expect it to.
Specifically, I get messages like this:
(gdb) run bunny
Starting program: debug/our_cvfh bunny
libc++abi.dylib: terminating
[New Thread 0x170b of process 80178]
Program received signal SIGABRT, Aborted.
0x00007fff88c6f866 in ?? ()
(gdb) bt
#0 0x00007fff88c6f866 in ?? ()
#1 0x00007fff8bb5235c in ?? ()
#2 0x0000000000000000 in ?? ()
(gdb) break rec/registered_views_source.h:305
Cannot access memory at address 0x961d60
In this case, I know where the error is, but I'd like to be able to backtrace it and see what called the function in this case.
Is PCL creating another thread, and that's why I can't backtrace? I'm not doing any visualizations right now, so I can't figure out why it'd be using threading.
I've also tried running the program in the debug directory instead of from my source root directory. Here's another example of it not working:
$ gdb our_cvfh
GNU gdb (GDB) 7.7
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-apple-darwin13.1.0".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from our_cvfh...done.
run (gdb) run
Starting program: /Users/jwoods/Projects/lidargl/fpfh/debug/our_cvfh
[New Thread 0x170b of process 33571]
Program received signal SIGSEGV, Segmentation fault.
0x000000010016cdec in ?? ()
(gdb) bt
#0 0x000000010016cdec in ?? ()
#1 0x00007fff5fbfbd08 in ?? ()
#2 0x00007fff5fbfbcc0 in ?? ()
#3 0x00007fff5fbfbcc8 in ?? ()
#4 0x00007fff5fbfbcc8 in ?? ()
#5 0x00007fff5fbfbcc8 in ?? ()
#6 0xffffffffffffffff in ?? ()
#7 0x00007fff5fbfbcc8 in ?? ()
#8 0x00007fff5fbfbcc8 in ?? ()
#9 0x00007fff5fbfbcc0 in ?? ()
#10 0x00007fff5fbfbcc0 in ?? ()
#11 0x00007fff5fbfbcc8 in ?? ()
#12 0x00007fff5fbfbcc8 in ?? ()
#13 0x00007fff5fbfbcc8 in ?? ()
#14 0x00007fff5fbff4a8 in ?? ()
#15 0x00007fff5fbff4d8 in ?? ()
#16 0x00007fff5fbff420 in ?? ()
#17 0x00007fff5fbff4d8 in ?? ()
#18 0x0000000000000000 in ?? ()
(gdb)
gdb works fine when I'm not using CMake, so my guess is it has something to do with CMake. This doesn't seem to present a problem for anyone else, which tells me it may also have to do with the fact that I'm using CMake with Mac OS X.
How do I get my normal GDB behavior?
Update
I can run dsymutil my_output_binary in order to generate the debugging symbols (following make). This is a workaround. I'd like it to be done automatically, and amn't sure why it's not. The dsymutil strategy works for most segfaults, but doesn't work for some cases of SIGABRT. Here is the output:
Calling compute <--- normal std::cerr output of my program, single-threaded
Assertion failed: (index >= 0 && index < size()), function operator[], file /usr/local/Cellar/eigen/3.2.1/include/eigen3/Eigen/src/Core/DenseCoeffsBase.h, line 378.
[New Thread 0x170b of process 64108]
Program received signal SIGABRT, Aborted.
0x00007fff84999866 in ?? ()
(gdb) bt
#0 0x00007fff84999866 in ?? ()
#1 0x00007fff862c335c in ?? ()
#2 0x0000000000000000 in ?? ()
(gdb) info threads
Id Target Id Frame
2 Thread 0x170b of process 64108 0x00007fff8499a662 in ?? ()
* 1 Thread 0x1503 of process 64108 0x00007fff84999866 in ?? ()
(gdb)
Note that my program is not itself multi-threaded, but it appears to be making use of a library which is creating threads — or at least that's what I gather from the gdb output.
I've tried disabling threading with Eigen, which is used by PCL.
Interestingly, lldb is able to generate the backtraces, but I'm curious as to why GDB can't.
Other than simply using LLDB in lieu of GDB, I haven't figured out how to debug threads.
However, I have figured out how to automatically produce debugging symbols! Hooray.
You need three files:
UseCompVer.cmake
AddOptions.cmake
UseDebugSymbols.cmake
Put these in your project's cmake/Modules/ directory.
In CMakeLists.txt, you'll need the following lines after your project declaration:
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/Modules/")
if (APPLE) # this if statement is optional, but you definitely need the include()
include(UseDebugSymbols)
endif (APPLE)
That will look in the appropriate location.
I should note that I made another change to my project simultaneously, which may also be useful to you. In my add_executable line, right after the name of the target, I added MACOSX_BUNDLE. This flag will cause it to be compiled as a .app instead of a regular binary. Here's an example from my project:
add_executable(pose MACOSX_BUNDLE pose.cpp rec/global_nn_recognizer_cvfh.cpp rec/global_nn_recognizer_cvfh.hpp rec/render_views_tesselated_sphere.cpp)