Friday, June 10, 2011

Why RtlCaptureContext crashes in release build

I ran into the issue that RtlCaptureContext crashes in release build.The crash is due to the access violation as following,

(1670.944): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=0013f264 ecx=0013f264 edx=00000040 esi=0013f6d4 edi=7c432881
eip=7c9033bc esp=0013f24c ebp=0000000f iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010212
ntdll!RtlpCaptureContext+0x72:
7c9033bc 8b4504          mov     eax,dword ptr [ebp+4] ss:0023:00000013=????????


Interestingly, ebp does not seem to contain a valid address.

Then I unassembled the RtlCaptureContext as the following,
0:000> uf ntdll!RtlpCaptureContext
ntdll!RtlpCaptureContext:
7c90334a 53              push    ebx
7c90334b 8b5c2408        mov     ebx,dword ptr [esp+8]
7c90334f c783b000000000000000 mov dword ptr [ebx+0B0h],0
7c903359 c783ac00000000000000 mov dword ptr [ebx+0ACh],0
7c903363 c783a800000000000000 mov dword ptr [ebx+0A8h],0
7c90336d c783a400000000000000 mov dword ptr [ebx+0A4h],0
7c903377 c783a000000000000000 mov dword ptr [ebx+0A0h],0
7c903381 c7839c00000000000000 mov dword ptr [ebx+9Ch],0
7c90338b 668c8bbc000000  mov     word ptr [ebx+0BCh],cs
7c903392 668c9b98000000  mov     word ptr [ebx+98h],ds
7c903399 668c8394000000  mov     word ptr [ebx+94h],es
7c9033a0 668ca390000000  mov     word ptr [ebx+90h],fs
7c9033a7 668cab8c000000  mov     word ptr [ebx+8Ch],gs
7c9033ae 668c93c8000000  mov     word ptr [ebx+0C8h],ss
7c9033b5 9c              pushfd
7c9033b6 8f83c0000000    pop     dword ptr [ebx+0C0h]
7c9033bc 8b4504          mov     eax,dword ptr [ebp+4]   <<<<<<< access violation here
7c9033bf 8983b8000000    mov     dword ptr [ebx+0B8h],eax
7c9033c5 8b4500          mov     eax,dword ptr [ebp]
7c9033c8 8983b4000000    mov     dword ptr [ebx+0B4h],eax
7c9033ce 8d4508          lea     eax,[ebp+8]
7c9033d1 8983c4000000    mov     dword ptr [ebx+0C4h],eax
7c9033d7 5b              pop     ebx
7c9033d8 c20400          ret     4


RtlCaptureContext does not set up ebp as the frame pointer at its prologue, so it uses ebp from the caller. It also means somehow the caller does not handle the ebp properly. Then I found the optimization option for the release build is Maximize Speed (/O2) in VC IDE. Since Maximize Speed (/O2) implicitly enables the option -- Omit Frame Pointers, the instructions to set up ebp as the frame pointer at the function prologue have been removed from the build. Ebp could contain any random value. So to use RtlCatpureContext in the release build, do not enable the option -- Omit Frame Pointers. Otherwise, the application will crash randomly in the release build.

In my case, one approach to resolve the crash is to add /Oy- to explicitly disable the option -- Omit Frame Pointers.

Monday, April 25, 2011

Tuesday, August 17, 2010

Track memory leak using Purify

Tracking memory leak from a child process of a window service requires a little bit setup upfront.

I worked on a defect regarding to memory leak from a child process of a window service recently. I used the following steps to allow Purify to track the memory usage in the child process.

1. Add the Purify product directory and the Purify cache directory to the system Path environment variable. For a system service, it is important to add these directories to the system Path environment variable, not just the user Path environment variable.

2. Instrument both window service and child process executable from the command line.
purify /run=no /replace=yes win_service.exe
purify /run=no /replace=yes child_process.exe

3. Add ServicesPipeTimeout registry entry to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control with a big DWORD value, i.e. 180000 (3 minutes) to allow instrumented service to start from the Service Control Panel.

4. Select the option Allow Service to Interact with Desktop for the service so that Purify is visible while the service is running.

5. Change the service startup type to Manual.

6. Launch the instrumented service application from the Service Control Panel. When the service runs, it brings up Purify so I could see data for both the service and child process displayed in separate error views.

Friday, August 6, 2010

A lost and found exception

These days, I am working on a defect related to a lost exception due to a catch-all handler as following,

try
{
    do_something();
}
catch (...)
{
    log_message();
}

From the log message, it indicates that some exception has been caught in the catch-all handler. However, I do not have any exception specific information, such as exception code and type. Without any exception specific information, it would be impossible to figure out the cause of the exception.

Then I think whether it is possible to intercept an exception before a handler handles it. On Windows, there is a run-time API to translate a Win32 exception to a C++ typed exception before a C++ exception handler handles it.

_se_translator_function _set_se_translator(_se_translator_function seTransFunction);

typedef void (*_se_translator_function)(unsigned int, struct _EXCEPTION_POINTERS *).

Before a Win32 exception is handled by a C++ catch handler, including a catch-all handler, _set_se_translator intercepts the Win32 exception and then process it based on the _se_translator_function provided.

So I change the code as following,

void new_trans_func( unsigned int uCode, EXCEPTION_POINTERS* pExp )
{
    // process the exception based on uCode and pExp
}

try
{
    _se_translator_function old_trans_func = _set_se_translator(new_trans_func);
    do_something();
    _set_se_translator(old_trans_func);
}
catch (...)
{
    log_message();
}

I enable /EHa, recompile the code, and set a breakpoint in new_trans_func. When the breakpoint is hit, I dump the exception code and record.

After knowing what exception has been thrown, I enable the debugger to break on the exception before it is handled by any exception handler.

Monday, July 5, 2010

STL and GDB

GDB has a better capability to display the content of STL templates than WinDBG.

The trick is the following script by Dr. Eng. Dan C. Marinescu

####### Begin of Script #########                                                                                                  
#   STL GDB evaluators/views/utilities - 1.03
#
#   The new GDB commands:                                                       
#         are entirely non instrumental                                           
#         do not depend on any "inline"(s) - e.g. size(), [], etc
#       are extremely tolerant to debugger settings
#                                                                               
#   This file should be "included" in .gdbinit as following:
#   source stl-views.gdb or just paste it into your .gdbinit file
#
#   The following STL containers are currently supported:
#
#       std::vector -- via pvector command
#       std::list
-- via plist or plist_member command
#       std::map
-- via pmap or pmap_member command
#       std::multimap
-- via pmap or pmap_member command
#       std::set
-- via pset command
#       std::multiset
-- via pset command
#       std::deque
-- via pdequeue command
#       std::stack
-- via pstack command
#       std::queue
-- via pqueue command
#       std::priority_queue
-- via ppqueue command
#       std::bitset
-- via pbitset command
#       std::string -- via pstring command
#       std::widestring -- via pwstring command
#
#   The end of this file contains (optional) C++ beautifiers
#   Make sure your debugger supports $argc
#
#   Simple GDB Macros writen by Dan Marinescu (H-PhD) - License GPL
#   Inspired by intial work of Tom Malnar,
#     Tony Novac (PhD) / Cornell / Stanford,
#     Gilad Mishne (PhD) and Many Many Others.
#   Contact: dan_c_marinescu@yahoo.com (Subject: STL)
#
#   Modified to work with g++ 4.3 by Anders Elton
#   Also added _member functions, that instead of printing the entire class in map, prints a member.

#
# std::vector<>
#

define pvector
    if $argc == 0
        help pvector
    else
        set $size = $arg0._M_impl._M_finish - $arg0._M_impl._M_start
        set $capacity = $arg0._M_impl._M_end_of_storage - $arg0._M_impl._M_start
        set $size_max = $size - 1
    end
    if $argc == 1
        set $i = 0
        while $i < $size
            printf "elem[%u]: ", $i
            p *($arg0._M_impl._M_start + $i)
            set $i++
        end
    end
    if $argc == 2
        set $idx = $arg1
        if $idx < 0 || $idx > $size_max
            printf "idx1, idx2 are not in acceptable range: [0..%u].\n", $size_max
        else
            printf "elem[%u]: ", $idx
            p *($arg0._M_impl._M_start + $idx)
        end
    end
    if $argc == 3
      set $start_idx = $arg1
      set $stop_idx = $arg2
      if $start_idx > $stop_idx
        set $tmp_idx = $start_idx
        set $start_idx = $stop_idx
        set $stop_idx = $tmp_idx
      end
      if $start_idx < 0 || $stop_idx < 0 || $start_idx > $size_max || $stop_idx > $size_max
        printf "idx1, idx2 are not in acceptable range: [0..%u].\n", $size_max
      else
        set $i = $start_idx
        while $i <= $stop_idx
            printf "elem[%u]: ", $i
            p *($arg0._M_impl._M_start + $i)
            set $i++
        end
      end
    end
    if $argc > 0
        printf "Vector size = %u\n", $size
        printf "Vector capacity = %u\n", $capacity
        printf "Element "
        whatis $arg0._M_impl._M_start
    end
end

document pvector
    Prints std::vector
information.
    Syntax: pvector

    Note: idx, idx1 and idx2 must be in acceptable range [0..
.size()-1].
    Examples:
    pvector v - Prints vector content, size, capacity and T typedef
    pvector v 0 - Prints element[idx] from vector
    pvector v 1 2 - Prints elements in range [idx1..idx2] from vector
end

#
# std::list<>
#

define plist
    if $argc == 0
        help plist
    else
        set $head = &$arg0._M_impl._M_node
        set $current = $arg0._M_impl._M_node._M_next
        set $size = 0
        while $current != $head
            if $argc == 2
                printf "elem[%u]: ", $size
                p *($arg1*)($current + 1)
            end
            if $argc == 3
                if $size == $arg2
                    printf "elem[%u]: ", $size
                    p *($arg1*)($current + 1)
                end
            end
            set $current = $current._M_next
            set $size++
        end
        printf "List size = %u \n", $size
        if $argc == 1
            printf "List "
            whatis $arg0
            printf "Use plist
to see the elements in the list.\n"
        end
    end
end

document plist
    Prints std::list
information.
    Syntax: plist
: Prints list size, if T defined all elements or just element at idx
    Examples:
    plist l - prints list size and definition
    plist l int - prints all elements and list size
    plist l int 2 - prints the third element in the list (if exists) and list size
end

define plist_member
    if $argc == 0
        help plist_member
    else
        set $head = &$arg0._M_impl._M_node
        set $current = $arg0._M_impl._M_node._M_next
        set $size = 0
        while $current != $head
            if $argc == 3
                printf "elem[%u]: ", $size
                p (*($arg1*)($current + 1)).$arg2
            end
            if $argc == 4
                if $size == $arg3
                    printf "elem[%u]: ", $size
                    p (*($arg1*)($current + 1)).$arg2
                end
            end
            set $current = $current._M_next
            set $size++
        end
        printf "List size = %u \n", $size
        if $argc == 1
            printf "List "
            whatis $arg0
            printf "Use plist_member
to see the elements in the list.\n"
        end
    end
end

document plist_member
    Prints std::list
information.
    Syntax: plist
: Prints list size, if T defined all elements or just element at idx
    Examples:
    plist_member l int member - prints all elements and list size
    plist_member l int member 2 - prints the third element in the list (if exists) and list size
end


#
# std::map and std::multimap
#

define pmap
    if $argc == 0
        help pmap
    else
        set $tree = $arg0
        set $i = 0
        set $node = $tree._M_t._M_impl._M_header._M_left
        set $end = $tree._M_t._M_impl._M_header
        set $tree_size = $tree._M_t._M_impl._M_node_count
        if $argc == 1
            printf "Map "
            whatis $tree
            printf "Use pmap
to see the elements in the map.\n"
        end
        if $argc == 3
            while $i < $tree_size
                set $value = (void *)($node + 1)
                printf "elem[%u].left: ", $i
                p *($arg1*)$value
                set $value = $value + sizeof($arg1)
                printf "elem[%u].right: ", $i
                p *($arg2*)$value
                if $node._M_right != 0
                    set $node = $node._M_right
                    while $node._M_left != 0
                        set $node = $node._M_left
                    end
                else
                    set $tmp_node = $node._M_parent
                    while $node == $tmp_node._M_right
                        set $node = $tmp_node
                        set $tmp_node = $tmp_node._M_parent
                    end
                    if $node._M_right != $tmp_node
                        set $node = $tmp_node
                    end
                end
                set $i++
            end
        end
        if $argc == 4
            set $idx = $arg3
            set $ElementsFound = 0
            while $i < $tree_size
                set $value = (void *)($node + 1)
                if *($arg1*)$value == $idx
                    printf "elem[%u].left: ", $i
                    p *($arg1*)$value
                    set $value = $value + sizeof($arg1)
                    printf "elem[%u].right: ", $i
                    p *($arg2*)$value
                    set $ElementsFound++
                end
                if $node._M_right != 0
                    set $node = $node._M_right
                    while $node._M_left != 0
                        set $node = $node._M_left
                    end
                else
                    set $tmp_node = $node._M_parent
                    while $node == $tmp_node._M_right
                        set $node = $tmp_node
                        set $tmp_node = $tmp_node._M_parent
                    end
                    if $node._M_right != $tmp_node
                        set $node = $tmp_node
                    end
                end
                set $i++
            end
            printf "Number of elements found = %u\n", $ElementsFound
        end
        if $argc == 5
            set $idx1 = $arg3
            set $idx2 = $arg4
            set $ElementsFound = 0
            while $i < $tree_size
                set $value = (void *)($node + 1)
                set $valueLeft = *($arg1*)$value
                set $valueRight = *($arg2*)($value + sizeof($arg1))
                if $valueLeft == $idx1 && $valueRight == $idx2
                    printf "elem[%u].left: ", $i
                    p $valueLeft
                    printf "elem[%u].right: ", $i
                    p $valueRight
                    set $ElementsFound++
                end
                if $node._M_right != 0
                    set $node = $node._M_right
                    while $node._M_left != 0
                        set $node = $node._M_left
                    end
                else
                    set $tmp_node = $node._M_parent
                    while $node == $tmp_node._M_right
                        set $node = $tmp_node
                        set $tmp_node = $tmp_node._M_parent
                    end
                    if $node._M_right != $tmp_node
                        set $node = $tmp_node
                    end
                end
                set $i++
            end
            printf "Number of elements found = %u\n", $ElementsFound
        end
        printf "Map size = %u\n", $tree_size
    end
end

document pmap
    Prints std::map
or std::multimap information. Works for std::multimap as well.
    Syntax: pmap : Prints map size, if T defined all elements or just element(s) with val(s)
    Examples:
    pmap m - prints map size and definition
    pmap m int int - prints all elements and map size
    pmap m int int 20 - prints the element(s) with left-value = 20 (if any) and map size
    pmap m int int 20 200 - prints the element(s) with left-value = 20 and right-value = 200 (if any) and map size
end


define pmap_member
    if $argc == 0
        help pmap_member
    else
        set $tree = $arg0
        set $i = 0
        set $node = $tree._M_t._M_impl._M_header._M_left
        set $end = $tree._M_t._M_impl._M_header
        set $tree_size = $tree._M_t._M_impl._M_node_count
        if $argc == 1
            printf "Map "
            whatis $tree
            printf "Use pmap to see the elements in the map.\n"
        end
        if $argc == 5
            while $i < $tree_size
                set $value = (void *)($node + 1)
                printf "elem[%u].left: ", $i
                p (*($arg1*)$value).$arg2
                set $value = $value + sizeof($arg1)
                printf "elem[%u].right: ", $i
                p (*($arg3*)$value).$arg4
                if $node._M_right != 0
                    set $node = $node._M_right
                    while $node._M_left != 0
                        set $node = $node._M_left
                    end
                else
                    set $tmp_node = $node._M_parent
                    while $node == $tmp_node._M_right
                        set $node = $tmp_node
                        set $tmp_node = $tmp_node._M_parent
                    end
                    if $node._M_right != $tmp_node
                        set $node = $tmp_node
                    end
                end
                set $i++
            end
        end
        if $argc == 6
            set $idx = $arg5
            set $ElementsFound = 0
            while $i < $tree_size
                set $value = (void *)($node + 1)
                if *($arg1*)$value == $idx
                    printf "elem[%u].left: ", $i
                    p (*($arg1*)$value).$arg2
                    set $value = $value + sizeof($arg1)
                    printf "elem[%u].right: ", $i
                    p (*($arg3*)$value).$arg4
                    set $ElementsFound++
                end
                if $node._M_right != 0
                    set $node = $node._M_right
                    while $node._M_left != 0
                        set $node = $node._M_left
                    end
                else
                    set $tmp_node = $node._M_parent
                    while $node == $tmp_node._M_right
                        set $node = $tmp_node
                        set $tmp_node = $tmp_node._M_parent
                    end
                    if $node._M_right != $tmp_node
                        set $node = $tmp_node
                    end
                end
                set $i++
            end
            printf "Number of elements found = %u\n", $ElementsFound
        end
        printf "Map size = %u\n", $tree_size
    end
end

document pmap_member
    Prints std::map or std::multimap information. Works for std::multimap as well.
    Syntax: pmap
: Prints map size, if T defined all elements or just element(s) with val(s)
    Examples:
    pmap_member m class1 member1 class2 member2 - prints class1.member1 : class2.member2
    pmap_member m class1 member1 class2 member2 lvalue - prints class1.member1 : class2.member2 where class1 == lvalue
end


#
# std::set and std::multiset
#

define pset
    if $argc == 0
        help pset
    else
        set $tree = $arg0
        set $i = 0
        set $node = $tree._M_t._M_impl._M_header._M_left
        set $end = $tree._M_t._M_impl._M_header
        set $tree_size = $tree._M_t._M_impl._M_node_count
        if $argc == 1
            printf "Set "
            whatis $tree
            printf "Use pset
to see the elements in the set.\n"
        end
        if $argc == 2
            while $i < $tree_size
                set $value = (void *)($node + 1)
                printf "elem[%u]: ", $i
                p *($arg1*)$value
                if $node._M_right != 0
                    set $node = $node._M_right
                    while $node._M_left != 0
                        set $node = $node._M_left
                    end
                else
                    set $tmp_node = $node._M_parent
                    while $node == $tmp_node._M_right
                        set $node = $tmp_node
                        set $tmp_node = $tmp_node._M_parent
                    end
                    if $node._M_right != $tmp_node
                        set $node = $tmp_node
                    end
                end
                set $i++
            end
        end
        if $argc == 3
            set $idx = $arg2
            set $ElementsFound = 0
            while $i < $tree_size
                set $value = (void *)($node + 1)
                if *($arg1*)$value == $idx
                    printf "elem[%u]: ", $i
                    p *($arg1*)$value
                    set $ElementsFound++
                end
                if $node._M_right != 0
                    set $node = $node._M_right
                    while $node._M_left != 0
                        set $node = $node._M_left
                    end
                else
                    set $tmp_node = $node._M_parent
                    while $node == $tmp_node._M_right
                        set $node = $tmp_node
                        set $tmp_node = $tmp_node._M_parent
                    end
                    if $node._M_right != $tmp_node
                        set $node = $tmp_node
                    end
                end
                set $i++
            end
            printf "Number of elements found = %u\n", $ElementsFound
        end
        printf "Set size = %u\n", $tree_size
    end
end

document pset
    Prints std::set
or std::multiset information. Works for std::multiset as well.
    Syntax: pset
: Prints set size, if T defined all elements or just element(s) having val
    Examples:
    pset s - prints set size and definition
    pset s int - prints all elements and the size of s
    pset s int 20 - prints the element(s) with value = 20 (if any) and the size of s
end

#
# std::dequeue
#

define pdequeue
    if $argc == 0
        help pdequeue
    else
        set $size = 0
        set $start_cur = $arg0._M_impl._M_start._M_cur
        set $start_last = $arg0._M_impl._M_start._M_last
        set $start_stop = $start_last
        while $start_cur != $start_stop
            p *$start_cur
            set $start_cur++
            set $size++
        end
        set $finish_first = $arg0._M_impl._M_finish._M_first
        set $finish_cur = $arg0._M_impl._M_finish._M_cur
        set $finish_last = $arg0._M_impl._M_finish._M_last
        if $finish_cur < $finish_last
            set $finish_stop = $finish_cur
        else
            set $finish_stop = $finish_last
        end
        while $finish_first != $finish_stop
            p *$finish_first
            set $finish_first++
            set $size++
        end
        printf "Dequeue size = %u\n", $size
    end
end

document pdequeue
    Prints std::dequeue
information.
    Syntax: pdequeue
: Prints dequeue size, if T defined all elements
    Deque elements are listed "left to right" (left-most stands for front and right-most stands for back)
    Example:
    pdequeue d - prints all elements and size of d
end

#
# std::stack
#

define pstack
    if $argc == 0
        help pstack
    else
        set $start_cur = $arg0.c._M_impl._M_start._M_cur
        set $finish_cur = $arg0.c._M_impl._M_finish._M_cur
        set $size = $finish_cur - $start_cur
        set $i = $size - 1
        while $i >= 0
            p *($start_cur + $i)
            set $i--
        end
        printf "Stack size = %u\n", $size
    end
end

document pstack
    Prints std::stack
information.
    Syntax: pstack
: Prints all elements and size of the stack
    Stack elements are listed "top to buttom" (top-most element is the first to come on pop)
    Example:
    pstack s - prints all elements and the size of s
end

#
# std::queue
#

define pqueue
    if $argc == 0
        help pqueue
    else
        set $start_cur = $arg0.c._M_impl._M_start._M_cur
        set $finish_cur = $arg0.c._M_impl._M_finish._M_cur
        set $size = $finish_cur - $start_cur
        set $i = 0
        while $i < $size
            p *($start_cur + $i)
            set $i++
        end
        printf "Queue size = %u\n", $size
    end
end

document pqueue
    Prints std::queue
information.
    Syntax: pqueue
: Prints all elements and the size of the queue
    Queue elements are listed "top to bottom" (top-most element is the first to come on pop)
    Example:
    pqueue q - prints all elements and the size of q
end

#
# std::priority_queue
#

define ppqueue
    if $argc == 0
        help ppqueue
    else
        set $size = $arg0.c._M_impl._M_finish - $arg0.c._M_impl._M_start
        set $capacity = $arg0.c._M_impl._M_end_of_storage - $arg0.c._M_impl._M_start
        set $i = $size - 1
        while $i >= 0
            p *($arg0.c._M_impl._M_start + $i)
            set $i--
        end
        printf "Priority queue size = %u\n", $size
        printf "Priority queue capacity = %u\n", $capacity
    end
end

document ppqueue
    Prints std::priority_queue
information.
    Syntax: ppqueue
: Prints all elements, size and capacity of the priority_queue
    Priority_queue elements are listed "top to buttom" (top-most element is the first to come on pop)
    Example:
    ppqueue pq - prints all elements, size and capacity of pq
end

#
# std::bitset
#

define pbitset
    if $argc == 0
        help pbitset
    else
        p /t $arg0._M_w
    end
end

document pbitset
    Prints std::bitset
information.
    Syntax: pbitset
: Prints all bits in bitset
    Example:
    pbitset b - prints all bits in b
end

#
# std::string
#

define pstring
    if $argc == 0
        help pstring
    else
        printf "String \t\t\t= \"%s\"\n", $arg0._M_data()
        printf "String size/length \t= %u\n", $arg0._M_rep()._M_length
        printf "String capacity \t= %u\n", $arg0._M_rep()._M_capacity
        printf "String ref-count \t= %d\n", $arg0._M_rep()._M_refcount
    end
end

document pstring
    Prints std::string information.
    Syntax: pstring

    Example:
    pstring s - Prints content, size/length, capacity and ref-count of string s
end

#
# std::wstring
#

define pwstring
    if $argc == 0
        help pwstring
    else
        call printf("WString \t\t= \"%ls\"\n", $arg0._M_data())
        printf "WString size/length \t= %u\n", $arg0._M_rep()._M_length
        printf "WString capacity \t= %u\n", $arg0._M_rep()._M_capacity
        printf "WString ref-count \t= %d\n", $arg0._M_rep()._M_refcount
    end
end

document pwstring
    Prints std::wstring information.
    Syntax: pwstring

    Example:
    pwstring s - Prints content, size/length, capacity and ref-count of wstring s
end

#
# C++ related beautifiers (optional)
#

set print pretty on
set print object on
set print static-members on
set print vtbl on
set print demangle on
set demangle-style gnu-v3
set print sevenbit-strings off

################## End of Script #####################


Since I do not have .gdbinit in my home folder, I create one, copy the whole script to it, then run a simple sample program.

Breakpoint 1, main () at stl_vector.cpp:15
(gdb) pvector
Prints std::vector information.
Syntax: pvector
Note: idx, idx1 and idx2 must be in acceptable range [0...size()-1].
Examples:
pvector v - Prints vector content, size, capacity and T typedef
pvector v 0 - Prints element[idx] from vector
pvector v 1 2 - Prints elements in range [idx1..idx2] from vector
(gdb) pvector iv
elem[0]: $1 = 10
elem[1]: $2 = 20
elem[2]: $3 = 30
Vector size = 3
Vector capacity = 4
Element type = int *
(gdb) pvector iv 0
elem[0]: $4 = 10
Vector size = 3
Vector capacity = 4
Element type = int *
(gdb)

Pretty amazing!

How to generate g++ symbol file

To generate g++ debug symbol for the build target, -g option could be used. However, the debug symbol is embedded in the target. A different approach is taken on Windows -- PDB file, not the module self, contains the debug symbol. In the field, WinDGB could load the PDB file for the release build without copying over debug build of the module.

The link here provides some information on how to strip g++ debug symbol from the build target into a symbol file.

Create the shared libraries containing the debug symbol.

$ g++ -g -c -fPIC lib1.cpp -o lib1.o
$ g++ -shared -o liblib1.so lib1.o

$ g++ -g -c -fPIC lib2.cpp -o lib2.o
$ g++ -shared -o liblib2.so lib2.o

Extract the debug symbol from the shared libraries into symbol files.

$ ./xsym.sh liblib1.so debug lib1.sym
$ ./xsym.sh liblib2.so debug lib2.sym

Link to the shared libraries.
 $ g++ -g main.cpp -o main -L. liblib1.so liblib2.so

Extract the debug symbol from the executable into a symbol files.
$ ./xsym.sh main.exe debug main.sym

A bash script -- xsym.sh is used to extract the debug symbol into a file.

#!/bin/bash
if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]; then
    echo Usage: tostripfile debugdir debugfile
    exit
fi
tostripfile=$1
debugdir=$2
debugfile=$3
if [ ! -d "$debugdir" ]; then
    echo creating directory: $debugdir
    mkdir -p $debugdir
fi
objcopy --only-keep-debug $tostripfile $debugdir/$debugfile
strip --strip-debug --strip-unneeded $tostripfile
objcopy --add-gnu-debuglink=$debugdir/$debugfile $tostripfile

Now the debug symbol files are in the debug folder.

Generate the release version of shared libraries and executable.
$ g++ -c -fPIC lib1.cpp -o lib1.o
$ g++ -c -fPIC lib2.cpp -o lib2.o
$ g++ -shared -o liblib1.so lib1.o
$ g++ -shared -o liblib2.so lib2.o
$ g++ main.cpp -o main -L. liblib1.so liblib2.so

Run the release version of executable through GDB using the debug symbol files
...
(no debugging symbols found)
(gdb) symbol-file debug/main.sym
Reading symbols from /xxx/debug/main.sym...done.
(gdb) break main
Breakpoint 1 at 0x401065: file main.cpp, line 6.
(gdb) r
Starting program: /xxx/main.exe
[New thread 4676.0x35fc]
[New thread 4676.0x4d50]

Breakpoint 1, main (argc=1, argv=0x1004a4e0) at main.cpp:6
(gdb) info shared
From        To          Syms Read   Shared Object Library
0x7c901000  0x7c9b1eb4  Yes         /c/WINDOWS/system32/ntdll.dll
0x7c801000  0x7c8f4c10  Yes         /c/WINDOWS/system32/kernel32.dll
0x61001000  0x61300000  Yes         /usr/bin/cygwin1.dll
0x77dd1000  0x77e6ab14  Yes         /c/WINDOWS/system32/advapi32.dll
0x77e71000  0x77f01464  Yes         /c/WINDOWS/system32/rpcrt4.dll
0x77fe1000  0x77ff0880  Yes         /c/WINDOWS/system32/secur32.dll
0x10001000  0x100060f4  Yes         /xxx/liblib2.so
0x003e1000  0x003e60f4  Yes         /xxx/liblib1.so
(gdb) add-symbol-file debug/lib1.sym 0x003e1000
add symbol table from file "debug/lib1.sym" at
    .text_addr = 0x3e1000
(y or n) y
Reading symbols from /xxx/debug/lib1.sym...done.
(gdb) add-symbol-file debug/lib2.sym 0x10001000
add symbol table from file "debug/lib2.sym" at
    .text_addr = 0x10001000
(y or n) y
Reading symbols from /xxx/debug/lib2.sym...done.
(gdb) s
(gdb) s
print1 () at lib1.cpp:5
(gdb) s
This is a message from lib1
(gdb) s
main (argc=1, argv=0x1004a4e0) at main.cpp:8
(gdb) s
print2 () at lib2.cpp:5
(gdb) s
This is a message from lib2
(gdb) s
main (argc=1, argv=0x1004a4e0) at main.cpp:9
(gdb)

After loading the debug symbol files, we could step into the code in the executable and shared libraries.

Friday, June 4, 2010

Java theory and practice series

I ran into Java theory and practice series by Brian Goetz these days. It provides tons of useful information and tips regarding to design and programming in Java.