rainers / cv2pdb

converter of DMD CodeView/DWARF debug information to PDB files
Artistic License 2.0
472 stars 109 forks source link

debug gcc #30

Open krylovgeorgii opened 6 years ago

krylovgeorgii commented 6 years ago

How to debug a program compiled with gcc?

I try to build .dll by gcc with -gdwarf option and use cv2pdb with -C option: Visual Studio load pdb symbols but cant go into functions from .dll by F11

rainers commented 6 years ago

Are you able to set breakpoints?

You can use https://github.com/Microsoft/microsoft-pdb/tree/master/cvdump to dump the resulting PDB file and verify whether it contains sensible line numbers.

krylovgeorgii commented 6 years ago

Thanks, it works if put breakpoint in source file but sometimes after F10 it returns "Source not available" - is it expected behaviour? and some variables not indicate - how i can improv level of debugging? maybe need some additional flags? (it looks like release with debug info)

leavittx commented 6 years ago

64-bit mingw is used here. And by "indicate variables" watches/locals/autos in Visual Studio debugger are meant I assume. They are not available in most cases, although it's written in the Features section:

So it should be supported. May there be a problem in lack of dwarf debug info in the source dll/exe? Which are the recommended mingw options in that case?

The source language for the library we're trying to debug is C, not C++. And we link that to a VS C++ program. Will posting cvdump output be any helpful?

It's already very nice that we're able to set breakpoints and see call stack I think, but full-featured debugging is what we seek for. Thank you!

rainers commented 6 years ago

I suspect it very much depends on the debug info emitted by gcc. If you can provide an example executable/DLL that you want to convert and specify which functions' locals don't work I can have a look.

There has been a recent improvement to locals in https://github.com/rainers/cv2pdb/pull/24 which has not yet made it into the downloadable executable. I just triggered a new build, should be available in a couple of minutes.

krylovgeorgii commented 6 years ago

I try to use new downloadable executable - it crashed on the files with that previous downloadable executable works well use cflags -gdwarf -O0

rainers commented 6 years ago

I just tried gcc (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 7.3.0 and it worked ok for me. What toolchain do you use? Could you provide an example executable?

leavittx commented 6 years ago

while @krylovgeorgii is working on getting executables, may I ask, which dwarf version should we use for the dwarf debug info? gcc defaults to version 4, but version 5 (experimental) also could be used. Found here

krylovgeorgii commented 6 years ago

Example of .dll with that that last downloadable executable crashed but previous works https://drive.google.com/file/d/1m-Pm4Ah8_43hNoxEV0sTzklHzcnueIfq/view?usp=sharing

leavittx commented 6 years ago

@rainers we use gcc (Rev1, Built by MSYS2 project) 7.3.0 And we have complete scripts to build the mpv dll (it's a video playback library) here

I don't know if it's interesting for you, but just cloning the repository and running build_debug.bat script should be enough to build all the files (it takes about an hour). mpv-1.dll will be put to the build\mpv-git\build folder in the end.

If you don't want to bother with it, maybe you want us to prepare an archive with sources included, so you could try some debugging on your machine?

rainers commented 6 years ago

I've fixed the regression in the last version and another older issue. Should be already available as download.

cv2pdb has very little support for DWARF3 and 4, but your dump uses DW_FORM_sec_offset which is DWARF4 (the DLL name suggests otherwise, though). I added a bit of support for that but I guess it won't help too much because a lot of functions seem to be built with frame-pointer-omission. cv2pdb needs a frame pointer for local variables.

So I'd suggest trying to build with -gdwarf2 -fno-omit-frame-pointer.

krylovgeorgii commented 6 years ago

I build with flags " -O0 -gdwarf-2 -fno-omit-frame-pointer " but locals variables dont works builded dll https://drive.google.com/open?id=1m-Pm4Ah8_43hNoxEV0sTzklHzcnueIfq

rainers commented 6 years ago

Hmmm, this DLL still doesn't use the frame pointer very often to address local variables. For example the output of objdump -W for function mp_aframe_pool_allocate shows:

<1><7c37>: Abbrev Number: 28 (DW_TAG_subprogram)
    <7c38>   DW_AT_external    : 1
    <7c39>   DW_AT_name        : mp_aframe_pool_allocate
    <7c51>   DW_AT_decl_file   : 1
    <7c52>   DW_AT_decl_line   : 540
    <7c54>   DW_AT_prototyped  : 1
    <7c55>   DW_AT_type        : <0x66a6>
    <7c59>   DW_AT_low_pc      : 0x1400023d0
    <7c61>   DW_AT_high_pc     : 0x140002547
    <7c69>   DW_AT_frame_base  : 0x871 (location list)
    <7c6d>   DW_AT_GNU_all_call_sites: 1
    <7c6e>   DW_AT_sibling     : <0x7ea5>
 <2><7c72>: Abbrev Number: 29 (DW_TAG_formal_parameter)
    <7c73>   DW_AT_name        : pool
    <7c78>   DW_AT_decl_file   : 1
    <7c79>   DW_AT_decl_line   : 540
    <7c7b>   DW_AT_type        : <0x7ea5>
    <7c7f>   DW_AT_location    : 0x988 (location list)
 <2><7c83>: Abbrev Number: 30 (DW_TAG_formal_parameter)
    <7c84>   DW_AT_name        : (indirect string, offset: 0x2d0): frame
    <7c88>   DW_AT_decl_file   : 1
    <7c89>   DW_AT_decl_line   : 540
    <7c8b>   DW_AT_type        : <0x7eab>
    <7c8f>   DW_AT_location    : 0x9e7 (location list)
 <2><7c93>: Abbrev Number: 30 (DW_TAG_formal_parameter)
    <7c94>   DW_AT_name        : (indirect string, offset: 0x217): samples
    <7c98>   DW_AT_decl_file   : 1
    <7c99>   DW_AT_decl_line   : 541
    <7c9b>   DW_AT_type        : <0x66a6>
    <7c9f>   DW_AT_location    : 0xa46 (location list)
...

and the respective location lists that describe where to find the variables:

    00000988 00000001400023d0 00000001400023f4 (DW_OP_reg2 (rcx))
    0000099b 00000001400023f4 000000014000244b (DW_OP_reg12 (r12))
    000009ae 000000014000244b 0000000140002453 (DW_OP_GNU_entry_value: (DW_OP_reg2 (rcx)); DW_OP_stack_value)
    000009c4 0000000140002453 0000000140002547 (DW_OP_reg12 (r12))
    000009d7 <End of list>
    000009e7 00000001400023d0 00000001400023fe (DW_OP_reg1 (rdx))
    000009fa 00000001400023fe 0000000140002451 (DW_OP_reg15 (r15))
    00000a0d 0000000140002451 0000000140002453 (DW_OP_GNU_entry_value: (DW_OP_reg1 (rdx)); DW_OP_stack_value)
    00000a23 0000000140002453 0000000140002547 (DW_OP_reg15 (r15))
    00000a36 <End of list>
    00000a46 00000001400023d0 00000001400023fe (DW_OP_reg8 (r8))
    00000a59 00000001400023fe 0000000140002449 (DW_OP_reg5 (rdi))
    00000a6c 0000000140002449 0000000140002453 (DW_OP_GNU_entry_value: (DW_OP_reg8 (r8)); DW_OP_stack_value)
    00000a82 0000000140002453 0000000140002547 (DW_OP_reg5 (rdi))

without any reference to the usual frame pointer rbp. It also uses some GNU extension which is said to be DWARF5. Maybe -gstrict-dwarf can help with the latter.

leavittx commented 6 years ago

Seems like -gstrict-dwarf doesn't change anything. Just in case: how do we check for this DWARF5 info presence?

It's weird that rbp isn't used. Maybe our compilation flags are messed up (I think gcc uses latest -O flag occurrence provided, which could also be overrided in the makefile). Looking into that currently.

To be sure that our toolchain is "sane", I think it makes sense to compile a smaller library and see if it works. Do you have by change any sample library/executable that you use for tests of new cv2pdb versions? Otherwise I will pick up a random one.

Btw we could make some documentation on cv2pdb troubleshooting in the end, so other people would know what to look for (cvdump/objdump/etc). Much thanks!

rainers commented 6 years ago

Any random small program should be fine. I have used this simple program for some regression tests in the past:

// test for cv2pdb
int x = 4;
int y = 5;
int z = 6;

struct S
{
    int f;
    int g;
    int h;

    static S create()
    {
        S s = { 2, 4, 9 };
        return s;
    }
    void square()
    {
        f *= f;
        g *= g;
        h *= h;
    }
};

class C : public S
{
public:
    int i;
    int j;
    static long long ll;

    C() : S(S::create())
    {
        square();
    }

    static int mul2(int z)
    {
        return 2*z;
    }
    virtual void foo(int z)
    {
        ll = mul2(z);
    }
};

long long C::ll = 5;

class D : public C
{
public:
    int k;

    virtual void foo(int z)
    {
        C::foo(z);
    }
};

int noargs()
{
    int a = 1;
    int b = 2;
    int c = 3;
    return a + b + c;
}

int main(int argc, char** argv)
{
    int arr[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
    S s = { 1, 3, 5 };
    D* d = new D;
    *(S*)d = s;
    d->i = 8;
    d->j = 9;
    d->k = 10;
    d->foo(7);

    noargs();
    int a = 1;
    int b = 2;
    int c = 3;
    int *m = 0;
    *m = argc;
    return c;
}

Just in case: how do we check for this DWARF5 info presence?

I run objdup -W mpv.dll >dwarf and then search for a function name in that dump, e.g. mp_aframe_pool_allocate as in the example above. A bit below you find info for parameters (DW_TAG_formal_parameter) and variables (DW_TAG_variable) with a description of their location (DW_AT_location). If it is a location list, the value is an offset within the .debug_loc section that you can also find in the dump.