Postgresql源码(148)hash表的调试方法与技巧
处理实际问题时,经常需要调试hash表,例如需要gdb查看hash表中的数据结构等等。本篇结合实例给出一些使用方法技巧,后续遇到问题可以快速通过gdb查看hash表内容。
gdb脚本
$hashtable
修改为当前关注的hash表:
set $hashtable = PortalHashTable
set $i = 0
while ($i <= $hashtable->hctl->max_bucket)set $seg_idx = $i / $hashtable->hctl->dsizeset $seg_off = $i % $hashtable->hctl->dsizeset $bucket = $hashtable->dir[$seg_idx][$seg_off]set $cnt = 0set $b = $bucketwhile ($b != 0)printf "bucket[%d] key_addr[%d]: %p\n", $i, $cnt, (char*)$b+sizeof(HASHELEMENT)set $cnt = $cnt + 1set $b = $b->linkendprintf "bucket[%d] has %d keys\n", $i, $cntset $i = $i + 1
end
实例一:查看PortalHashTable
例如执行下面存储过程,会产生5个游标portal和1个匿名快执行portal,例如在执行时,我想打印每一个portal结构里面的变量,可以执行上面gdb脚本。
create table test_table(id int, name text);
insert into test_table values(1, 'abc');DO $$
DECLAREcur1 CURSOR FOR SELECT id, name FROM test_table;cur2 CURSOR FOR SELECT id, name FROM test_table;cur3 CURSOR FOR SELECT id, name FROM test_table;cur4 CURSOR FOR SELECT id, name FROM test_table;cur5 CURSOR FOR SELECT id, name FROM test_table;rec1 RECORD;rec2 RECORD;rec3 RECORD;rec4 RECORD;rec5 RECORD;
BEGINOPEN cur1;FETCH cur1 INTO rec1;RAISE NOTICE 'cur1: id=%, name=%', rec1.id, rec1.name;OPEN cur2;FETCH cur2 INTO rec2;RAISE NOTICE 'cur2: id=%, name=%', rec2.id, rec2.name;OPEN cur3;FETCH cur3 INTO rec3;RAISE NOTICE 'cur3: id=%, name=%', rec3.id, rec3.name;OPEN cur4;FETCH cur4 INTO rec4;RAISE NOTICE 'cur4: id=%, name=%', rec4.id, rec4.name;OPEN cur5;FETCH cur5 INTO rec5;RAISE NOTICE 'cur5: id=%, name=%', rec5.id, rec5.name;perform pg_sleep(30);
END
$$;
执行
set $hashtable = PortalHashTable
set $i = 0
while ($i <= $hashtable->hctl->max_bucket)set $seg_idx = $i / $hashtable->hctl->dsizeset $seg_off = $i % $hashtable->hctl->dsizeset $bucket = $hashtable->dir[$seg_idx][$seg_off]set $cnt = 0set $b = $bucketwhile ($b != 0)printf "bucket[%d] key_addr[%d]: %p\n", $i, $cnt, (char*)$b+sizeof(HASHELEMENT)set $cnt = $cnt + 1set $b = $b->linkendset $i = $i + 1
end
执行结果
(gdb) set $hashtable = PortalHashTable
(gdb) set $i = 0
(gdb) while ($i <= $hashtable->hctl->max_bucket)> set $seg_idx = $i / $hashtable->hctl->dsize> set $seg_off = $i % $hashtable->hctl->dsize> set $bucket = $hashtable->dir[$seg_idx][$seg_off]> set $cnt = 0> set $b = $bucket> while ($b != 0)> printf "bucket[%d] key_addr[%d]: %p\n", $i, $cnt, (char*)$b+sizeof(HASHELEMENT)> set $cnt = $cnt + 1> set $b = $b->link> end> set $i = $i + 1>endbucket[1] key_addr[0]: 0x3751078
bucket[1] key_addr[1]: 0x3751020
bucket[2] key_addr[0]: 0x3750f70
bucket[5] key_addr[0]: 0x3750f18
bucket[6] key_addr[0]: 0x3750fc8
bucket[13] key_addr[0]: 0x37510d0
得到5个key值,结合hash表创建时使用的数据结构是PortalHashEnt
void
EnablePortalManager(void)
{HASHCTL ctl;Assert(TopPortalContext == NULL);TopPortalContext = AllocSetContextCreate(TopMemoryContext,"TopPortalContext",ALLOCSET_DEFAULT_SIZES);ctl.keysize = MAX_PORTALNAME_LEN;ctl.entrysize = sizeof(PortalHashEnt);PortalHashTable = hash_create("Portal hash", PORTALS_PER_USER,&ctl, HASH_ELEM);
}
可以把每个地址转换为PortalHashEnt后查看值:
(gdb) p *(PortalHashEnt*)0x3751078
$33 = {portalname = "<UNNAMED PORTAL 11>", '\000' <repeats 44 times>, portal = 0x374d790}
(gdb) p *(PortalHashEnt*)0x3751020
$34 = {portalname = "<UNNAMED PORTAL 13>", '\000' <repeats 44 times>, portal = 0x374d8b8}
(gdb) p *(PortalHashEnt*)0x3750f70
$35 = {portalname = "<UNNAMED PORTAL 12>", '\000' <repeats 44 times>, portal = 0x374db08}
(gdb) p *(PortalHashEnt*)0x3750f18
$36 = {portalname = "<UNNAMED PORTAL 14>", '\000' <repeats 44 times>, portal = 0x374dc30}
(gdb) p *(PortalHashEnt*)0x3750fc8
$37 = {portalname = "<UNNAMED PORTAL 15>", '\000' <repeats 44 times>, portal = 0x374d9e0}
(gdb) p *(PortalHashEnt*)0x37510d0
$38 = {portalname = '\000' <repeats 63 times>, portal = 0x374d668}(gdb) p *$38.portal
$40 = {name = 0x37510d0 "", prepStmtName = 0x0, portalContext = 0x3906c10, resowner = 0x37d3230, cleanup = 0x845ed8 <PortalCleanup>, createSubid = 1, activeSubid = 1,sourceText = 0x3664978 "DO $$\nDECLARE\n cur1 CURSOR FOR SELECT id, name FROM test_table;\n cur2 CURSOR FOR SELECT id, name FROM test_table;\n cur3 CURSOR FOR SELECT id, name FROM test_table;\n cur4 CURSOR FOR SELECT "...,safeSourceText = 0x3664978 "DO $$\nDECLARE\n cur1 CURSOR FOR SELECT id, name FROM test_table;\n cur2 CURSOR FOR SELECT id, name FROM test_table;\n cur3 CURSOR FOR SELECT id, name FROM test_table;\n cur4 CURSOR FOR SELECT "..., commandTag = 0x1803fed "DO", stmts = 0x3849840, cplan = 0x0, portalParams = 0x0, queryEnv = 0x0, strategy = PORTAL_MULTI_QUERY,cursorOptions = 4, run_once = 1 '\001', status = PORTAL_ACTIVE, portalPinned = 0 '\000', autoHeld = 0 '\000', queryDesc = 0x0, tupDesc = 0x0, formats = 0x0, holdStore = 0x0,holdContext = 0x0, holdSnapshot = 0x0, atStart = 1 '\001', atEnd = 1 '\001', portalPos = 0, creation_time = 808216570922698, visible = 0 '\000', failed_in_sub = 0 '\000',up_instrument = 0, cn_penetrate_tupleDesc = 0x0, last_fetched = 0, paused = 0 '\000', cursor = 0 '\000'}
实例二:查看共享内存hash表
hashtable修改为ShmemIndex后执行脚本:
set $hashtable = ShmemIndex
set $i = 0
printf "max_bucket[%d]\n", $hashtable->hctl->max_bucket
while ($i <= $hashtable->hctl->max_bucket)set $seg_idx = $i / $hashtable->hctl->dsizeset $seg_off = $i % $hashtable->hctl->dsizeset $bucket = $hashtable->dir[$seg_idx][$seg_off]set $cnt = 0set $b = $bucketwhile ($b != 0)printf "bucket[%d] key_addr[%d]: %p\n", $i, $cnt, (char*)$b+sizeof(HASHELEMENT)set $cnt = $cnt + 1set $b = $b->linkendprintf "bucket[%d] has %d keys\n", $i, $cntset $i = $i + 1
end
执行结果
max_bucket[439]
bucket[0] has 0 keys
bucket[1] key_addr[0]: 0x7f07221793c0
bucket[1] key_addr[1]: 0x7f075a7ac870
bucket[1] has 2 keys
bucket[2] key_addr[0]: 0x7f075a3e0d30
bucket[2] has 1 keys
bucket[3] has 0 keys
bucket[4] key_addr[0]: 0x7f075a014e10
bucket[4] key_addr[1]: 0x7f076ac262d0
bucket[4] has 2 keys
bucket[5] has 0 keys
bucket[6] key_addr[0]: 0x7f075a3dffc0
bucket[6] has 1 keys
bucket[7] has 0 keys
bucket[8] has 0 keys
bucket[9] key_addr[0]: 0x7f0759cd81b0
bucket[9] has 1 keys
bucket[10] has 0 keys
找到数据结构ShmemIndexEnt:
void
InitShmemIndex(void)
{HASHCTL info;int hash_flags;info.keysize = SHMEM_INDEX_KEYSIZE;info.entrysize = sizeof(ShmemIndexEnt);hash_flags = HASH_ELEM;ShmemIndex = ShmemInitHash("ShmemIndex",SHMEM_INDEX_SIZE, SHMEM_INDEX_SIZE,&info, hash_flags);
}
强制转换:
...
(gdb) p *(ShmemIndexEnt*)0x7f075a7ac870
$46 = {key = "Extension group 145", '\000' <repeats 28 times>, location = 0x7f075a929900, size = 77848}
(gdb) p *(ShmemIndexEnt*)0x7f075a3e0d30
$47 = {key = "Extension group 74", '\000' <repeats 29 times>, location = 0x7f075a3e0d80, size = 77848}
...