#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <libelf.h>
#include <libdwarf/libdwarf.h>
#include <libdwarf/dwarf.h>
#include <glib.h>

GHashTable *locals();

GHashTable *foo(){
    char *x = "1";
    char *y = "hello";
    return locals();
}

GHashTable *locals(){
    GHashTable *hash;
    int fd;
    Elf *elf;
    int ret;
    Dwarf_Debug dbg;
    Dwarf_Die cu_die;
    Dwarf_Die func_die;
    Dwarf_Die var_die;
    Dwarf_Die var_die_tmp;
    Dwarf_Error err;
    Dwarf_Unsigned cu_header_length = 0;
    Dwarf_Unsigned abbrev_offset = 0;
    Dwarf_Half version_stamp = 0;
    Dwarf_Half address_size = 0;
    Dwarf_Unsigned next_cu_offset = 0;
    Dwarf_Half tag;
    Dwarf_Attribute attr;
    char *name;
    Dwarf_Addr high_pc;
    Dwarf_Addr low_pc;
    int i;
    void *frame = __builtin_frame_address(1);
    unsigned long retaddr = (unsigned long)__builtin_return_address(0);

    hash = g_hash_table_new((GHashFunc)g_str_hash, (GCompareFunc)g_str_equal);
    elf_version(EV_CURRENT);
    fd = open("/proc/self/exe", O_RDONLY);
    elf = elf_begin(fd, ELF_C_READ, (Elf*)0);
    ret = dwarf_elf_init(elf, DW_DLC_READ, NULL, NULL, &dbg, &err);
    while ((ret = dwarf_next_cu_header(
                dbg, &cu_header_length, &version_stamp, &abbrev_offset,
                &address_size, &next_cu_offset, &err)) == DW_DLV_OK){
        cu_die = NULL;
        while(dwarf_siblingof(dbg, cu_die, &cu_die, &err) != DW_DLV_NO_ENTRY){
            dwarf_tag(cu_die, &tag, &err);
            if(tag != DW_TAG_compile_unit) continue;
            ret = dwarf_child(cu_die, &func_die, &err);
            if(ret != DW_DLV_OK) continue;
            do{ 
                dwarf_tag(func_die, &tag, &err);
                if(tag != DW_TAG_subprogram) continue;
                dwarf_attr(func_die, DW_AT_high_pc, &attr, &err);
                dwarf_formaddr(attr, &high_pc, &err);
                dwarf_attr(func_die, DW_AT_low_pc, &attr, &err);
                dwarf_formaddr(attr, &low_pc, &err);
                if(low_pc > retaddr || retaddr > high_pc) continue;
                ret = dwarf_child(func_die, &var_die, &err);
                if(ret != DW_DLV_OK) continue;
                i=0;
                var_die_tmp = var_die;
                while(dwarf_siblingof(dbg, var_die, &var_die, &err) != DW_DLV_NO_ENTRY) i++;
                var_die = var_die_tmp;
                do{ 
                    dwarf_tag(var_die, &tag, &err);
                    if(tag != DW_TAG_variable) continue;
                    dwarf_attr(var_die, DW_AT_name, &attr, &err);                                                 
                    dwarf_formstring(attr, &name, &err);                                                          
                    g_hash_table_insert(hash, strdup(name),
                                        strdup((frame - 4 - (4 * i--))));
                }while(dwarf_siblingof(dbg, var_die, &var_die, &err) != DW_DLV_NO_ENTRY);

            }while(dwarf_siblingof(dbg, func_die, &func_die, &err) != DW_DLV_NO_ENTRY);
        }
    }
    dwarf_finish(dbg, &err);
    elf_end(elf);
    close(fd);
    return hash;
}

int main(int argc, char *argv[]){
    GHashTable *hash = foo();
    printf("x=%s\n", *(char**)g_hash_table_lookup(hash, "x"));
    printf("y=%s\n", *(char**)g_hash_table_lookup(hash, "y"));
    g_hash_table_destroy(hash);
    return EXIT_SUCCESS;
}
