Eo gdb: add a way to resolve Eo ids from GDB without a running process

Normally when debugging Eo with gdb you can just use any of the internal
eo functions to resolve the id to its internal pointer. However, when
loading a coredump you can't execute any code, not even the id resolve
code.

This change adds a gdb function that resolves the id to its pointer form
without executing any code in the process space. This plugin is
essentially the id resolve code written in python as a gdb function.

Usage:
 Print the pointer:
 (gdb) print $eo_resolve(obj)
 $1 = (_Eo_Object *) 0x5555559bbe70

 Use it directly (e.g. to print the class name):
 (gdb) $eo_resolve(obj)->klass->desc.name

This plugin requires that the coredump would be loaded with the exact
same libeo.so binary (or at least one that hasn't changed eo internals),
and that the debug symbols for libeo.so would be available for gdb to
use.

Note:
This feature is incomplete and only resolves IDs that are owned by the
main thread and in the main domain. This is not a big issue at the
moment, because almost all of our IDs are like that.

@feature
This commit is contained in:
Tom Hacohen 2016-11-18 08:44:21 +00:00
parent 3dd51bf53d
commit 79d76fb25e
2 changed files with 108 additions and 2 deletions

View File

@ -1,4 +1,104 @@
# Implement eo_break that'll break on a macro/subid/whatever.
import gdb
"""
All of this script relies heavily on Eo internals and will break if they
change. Need to make sure this is always in sync.
"""
ptr_size = int(gdb.parse_and_eval('sizeof(void *)'))
SHIFT_MID_TABLE_ID = 0x30
MASK_MID_TABLE_ID = 0x7ff
SHIFT_TABLE_ID = 0x25
MASK_TABLE_ID = 0x7ff
SHIFT_ENTRY_ID = 0x1a
MASK_ENTRY_ID = 0x7ff
MASK_GENERATIONS = 0x3ffffff
MASK_OBJ_TAG = 0x4000000000000000
if ptr_size == 4:
# 32 bits
BITS_MID_TABLE_ID = 5
BITS_TABLE_ID = 5
BITS_ENTRY_ID = 11
BITS_GENERATION_COUNTER = 6
BITS_DOMAIN = 2
BITS_CLASS = 1
REF_TAG_SHIFT = 30
SUPER_TAG_SHIFT = 31
DROPPED_TABLES = 0
DROPPED_ENTRIES = 4
else:
# 64 bits
BITS_MID_TABLE_ID = 11
BITS_TABLE_ID = 11
BITS_ENTRY_ID = 11
BITS_GENERATION_COUNTER = 26
BITS_DOMAIN = 2
BITS_CLASS = 1
REF_TAG_SHIFT = 62
SUPER_TAG_SHIFT = 63
DROPPED_TABLES = 2
DROPPED_ENTRIES = 3
# /* Shifts macros to manipulate the Eo id */
SHIFT_DOMAIN = (BITS_MID_TABLE_ID + BITS_TABLE_ID +
BITS_ENTRY_ID + BITS_GENERATION_COUNTER)
SHIFT_MID_TABLE_ID = (BITS_TABLE_ID +
BITS_ENTRY_ID + BITS_GENERATION_COUNTER)
SHIFT_TABLE_ID = (BITS_ENTRY_ID + BITS_GENERATION_COUNTER)
SHIFT_ENTRY_ID = (BITS_GENERATION_COUNTER)
# /* Maximum ranges */
MAX_DOMAIN = (1 << BITS_DOMAIN)
MAX_MID_TABLE_ID = (1 << BITS_MID_TABLE_ID)
MAX_TABLE_ID = ((1 << BITS_TABLE_ID) - DROPPED_TABLES)
MAX_ENTRY_ID = ((1 << BITS_ENTRY_ID) - DROPPED_ENTRIES)
MAX_GENERATIONS = (1 << BITS_GENERATION_COUNTER)
# /* Masks */
MASK_DOMAIN = (MAX_DOMAIN - 1)
MASK_MID_TABLE_ID = (MAX_MID_TABLE_ID - 1)
MASK_TABLE_ID = ((1 << BITS_TABLE_ID) - 1)
MASK_ENTRY_ID = ((1 << BITS_ENTRY_ID) - 1)
MASK_GENERATIONS = (MAX_GENERATIONS - 1)
MASK_OBJ_TAG = (1 << (REF_TAG_SHIFT))
null_ptr = gdb.parse_and_eval('(_Eo_Object *) 0')
class Eo_resolve(gdb.Function):
def __init__(self):
gdb.Function.__init__(self, 'eo_resolve')
def invoke(self, arg):
obj_id = int(arg)
mid_table_id = (obj_id >> SHIFT_MID_TABLE_ID) & MASK_MID_TABLE_ID
table_id = (obj_id >> SHIFT_TABLE_ID) & MASK_TABLE_ID
entry_id = (obj_id >> SHIFT_ENTRY_ID) & MASK_ENTRY_ID
tag_bit = (obj_id) & MASK_OBJ_TAG
generation = obj_id & MASK_GENERATIONS
if (obj_id == 0) or (tag_bit == 0):
gdb.write('Pointer is NULL or not a valid object.\n')
return null_ptr
entries = gdb.parse_and_eval('_eo_gdb_main_domain->tables[0]->' +
'eo_ids_tables[{0}]'.format(mid_table_id))
if int(entries) == 0:
gdb.write('Pointer is not a valid object.\n')
return null_ptr
entry = entries[table_id]['entries'][entry_id]
if (not entry['active']) or (int(entry['generation']) != generation):
gdb.write('Pointer is no longer active.\n')
return null_ptr
return entry['ptr']
Eo_resolve()

View File

@ -1881,6 +1881,11 @@ _eo_table_del_cb(void *in)
_eo_free_ids_tables(data);
}
/* FIXME: Support other domains and tables, at the moment only the main
* domain and table.
* This is used by the gdb debug helper script */
Eo_Id_Data *_eo_gdb_main_domain = NULL;
EAPI Eina_Bool
efl_object_init(void)
{
@ -1946,6 +1951,7 @@ efl_object_init(void)
// specially force eoid data to be creanted so we can switch it to domain 0
Eo_Id_Data *data = _eo_table_data_new(EFL_ID_DOMAIN_MAIN);
_eo_gdb_main_domain = data;
if (!data)
{
EINA_LOG_ERR("Could not allocate main table data");