本节简单介绍了SetupLockInTable方法中与OOM相关的代码。
有时候我们可能会在PG的日志发现如下信息:
2020-01-09 16:29:19.062 CST,"pg12","testdb",6193,"[local]",5e16dccd.1831,1,"CREATE TABLE",2020-01-09 15:57:01 CST,2/34,1512004206,ERROR,53200,"out of shared memory",,"You might need to increase max_locks_per_transaction.",,,,"CREATE TABLE a13030 (id int);",,,"psql"
2020-01-09 16:29:19.379 CST,"pg12","testdb",6193,"[local]",5e16dccd.1831,2,"CREATE TABLE",2020-01-09 15:57:01 CST,2/0,1512004206,ERROR,25P02,"current transaction is aborted, commands ignored until end of transaction block",,,,,,"CREATE TABLE a13031 (id int);",,,"psql"
直观上来看,OOM似乎与max_locks_per_transaction扯不上什么关系,为什么PG会提示增加max_locks_per_transaction的值呢?
一、源码解读
测试脚本
\pset footer off
\pset tuples_only
\o /tmp/drop.sql
SELECT 'drop table if exists tbl' || id || ' ;' as "--"
FROM generate_series(1, 20000) AS id;
\i /tmp/drop.sql
\pset footer off
\pset tuples_only
\o /tmp/create.sql
SELECT 'CREATE TABLE tbl' || id || ' (id int);' as "--"
FROM generate_series(1, 20000) AS id;
\o /tmp/ret.txt
begin;
\i /tmp/create.sql
错误信息
“You might need to increase max_locks_per_transaction.”
出现在lock.c中,具体在该测试案例中,该错误信息出现在lock.c:965中... /* * Find or create lock and proclock entries with this tag * * Note: if the locallock object already existed, it might have a pointer * to the lock already ... but we should not assume that that pointer is * valid, since a lock object with zero hold and request counts can go * away anytime. So we have to use SetupLockInTable() to recompute the * lock and proclock pointers, even if they're already set. */ proclock = SetupLockInTable(lockMethodTable, MyProc, locktag, hashcode, lockmode); if (!proclock) { AbortStrongLockAcquire(); LWLockRelease(partitionLock); if (locallock->nLocks == 0) RemoveLocalLock(locallock); if (locallockp) *locallockp = NULL; if (reportMemoryError) ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of shared memory"), errhint("You might need to increase max_locks_per_transaction."))); else return LOCKACQUIRE_NOT_AVAIL; } ...
错误原因是SetupLockInTable返回了NULL。
SetupLockInTable
```
/*
- Find or create LOCK and PROCLOCK objects as needed for a new lock
- request.
- 对于新的lock申请,从HTAB中检索或者新创建相应的LOCK或PROCLOCK对象
* - Returns the PROCLOCK object, or NULL if we failed to create the objects
- for lack of shared memory.
- 返回PROCLOCK对象,如出现内存不足的问题,返回NULL。
* - The appropriate partition lock must be held at entry, and will be
- held at exit.
-
相应的分区lock必须在入口处持有,在退出时也将持有。
/
static PROCLOCK
SetupLockInTable(LockMethod lockMethodTable, PGPROC *proc,const LOCKTAG *locktag, uint32 hashcode, LOCKMODE lockmode)
{
LOCK lock;
PROCLOCK proclock;
PROCLOCKTAG proclocktag;
uint32 proclock_hashcode;
bool found;/*
- Find or create a lock with this tag.
- 检索或者创建lock(使用tag)。
/
lock = (LOCK ) hash_search_with_hash_value(LockMethodLockHash,(const void *) locktag, hashcode, HASH_ENTER_NULL, &found);
return NULL;
…
}
**hash_search_with_hash_value**
void
} /
Convert a hash value to a bucket number /
} (gdb) b LockAcquireExtended
Breakpoint 1, LockAcquireExtended (locktag=0x7ffdd23a8940, lockmode=3, sessionLock=false, dontWait=false,
[local:/data/run/pg12]:5120 pg12
@testdb=# select pid,mode,locktype,relation from pg_locks where pid = 1877;
[local:/data/run/pg12]:5120 pg12
@testdb=# (gdb) c
Breakpoint 1, SetupLockInTable (lockMethodTable=0xc8dba0
(gdb) b SetupLockInTable if lockmode == 8
Breakpoint 2, SetupLockInTable (lockMethodTable=0xc8dba0
```
hash_search_with_hash_value(HTAB hashp,
const void
keyPtr,
uint32 hashvalue,
HASHACTION action,
bool foundPtr)
{
HASHHDR
hctl = hashp->hctl;//获取HASHHDR
int freelist_idx = FREELIST_IDX(hctl, hashvalue);//根据hash值获取idx
Size keysize;//键值大小
uint32 bucket;//桶号
long segment_num;//段号
long segment_ndx;//
HASHSEGMENT segp;//段
HASHBUCKET currBucket;//当前桶号
HASHBUCKET prevBucketPtr;//上一个桶号
HashCompareFunc match;//是否match?
if HASH_STATISTICS//统计信息
hash_accesses++;
hctl->accesses++;
endif
/*
* If inserting, check if it is time to split a bucket.
* 如正插入,检查是否需要分裂哈希桶
*
* NOTE: failure to expand table is not a fatal error, it just means we
* have to run at higher fill factor than we wanted. However, if we're
* using the palloc allocator then it will throw error anyway on
* out-of-memory, so we must do this before modifying the table.
* 注意:扩展哈希表出现问题不是致命错误,只是意味着我们不得不执行比我们期望更高更高的填充因子.
* 但是,如果我们正在使用palloc分配器,那么只要出现内存溢出则会抛出错误,
* 因此我们不需在更新表前完成这个事情.
*/
if (action == HASH_ENTER || action == HASH_ENTER_NULL)
{
/*
* Can't split if running in partitioned mode, nor if frozen, nor if
* table is the subject of any active hash_seq_search scans. Strange
* order of these tests is to try to check cheaper conditions first.
* 如在分区模式/冻结/处于其他活动hash_seq_search扫描期间,则不能进行分裂.
* 奇怪的是,这些测试的顺序是先尝试检查成本更低的条件.
*/
if (!IS_PARTITIONED(hctl) && !hashp->frozen &&
hctl->freeList[0].nentries / (long) (hctl->max_bucket + 1) >= hctl->ffactor &&
!has_seq_scans(hashp))
(void) expand_table(hashp);
}
/*
* Do the initial lookup
* 执行初始化检索
*/
//计算桶号
bucket = calc_bucket(hctl, hashvalue);
//计算段号和段内编号
segment_num = bucket >> hashp->sshift;
segment_ndx = MOD(bucket, hashp->ssize);
//获取directory
segp = hashp->dir[segment_num];
if (segp == NULL)
hash_corrupted(hashp);
//记录桶号
prevBucketPtr = &segp[segment_ndx];
currBucket = *prevBucketPtr;
/*
* Follow collision chain looking for matching key
* 沿着哈希键冲突链搜索匹配键
*/
//匹配函数
match = hashp->match; /* save one fetch in inner loop */
//键大小
keysize = hashp->keysize; /* ditto */
while (currBucket != NULL)
{
if (currBucket->hashvalue == hashvalue &&
match(ELEMENTKEY(currBucket), keyPtr, keysize) == 0)
break;
prevBucketPtr = &(currBucket->link);
currBucket = *prevBucketPtr;
if HASH_STATISTICS
hash_collisions++;
hctl->collisions++;
endif
}
//结果赋值
if (foundPtr)
*foundPtr = (bool) (currBucket != NULL);
/*
* OK, now what?
* 根据action执行相关操作
*/
switch (action)
{
case HASH_FIND:
//搜索
if (currBucket != NULL)
return (void *) ELEMENTKEY(currBucket);
return NULL;
case HASH_REMOVE:
//移除
if (currBucket != NULL)
{
/* if partitioned, must lock to touch nentries and freeList */
//如分区,在访问条目入口和空闲链表时必须先请求锁
if (IS_PARTITIONED(hctl))
SpinLockAcquire(&(hctl->freeList[freelist_idx].mutex));
/* delete the record from the appropriate nentries counter. */
//修改nentries计数器
Assert(hctl->freeList[freelist_idx].nentries > 0);
hctl->freeList[freelist_idx].nentries--;
/* remove record from hash bucket's chain. */
//在哈希桶中链中删除记录
*prevBucketPtr = currBucket->link;
/* add the record to the appropriate freelist. */
//添加记录到正确的空闲链表上
currBucket->link = hctl->freeList[freelist_idx].freeList;
hctl->freeList[freelist_idx].freeList = currBucket;
if (IS_PARTITIONED(hctl))
//释放锁
SpinLockRelease(&hctl->freeList[freelist_idx].mutex);
/*
* better hope the caller is synchronizing access to this
* element, because someone else is going to reuse it the next
* time something is added to the table
* 调用者最好是同步访问元素,因为其他进程在下一次添加到哈希表可以复用.
*/
return (void *) ELEMENTKEY(currBucket);
}
return NULL;
case HASH_ENTER_NULL:
/* ENTER_NULL does not work with palloc-based allocator */
//验证分配器
Assert(hashp->alloc != DynaHashAlloc);
/* FALL THRU */
//继续往下执行
case HASH_ENTER:
/* Return existing element if found, else create one */
//如找到,则返回现存的元素,否则创建一个
if (currBucket != NULL)
return (void *) ELEMENTKEY(currBucket);
/* disallow inserts if frozen */
//如冻结,则不允许插入,报错
if (hashp->frozen)
elog(ERROR, "cannot insert into frozen hashtable \"%s\"",
hashp->tabname);
//获取当前桶
currBucket = get_hash_entry(hashp, freelist_idx);
if (currBucket == NULL)
{
//如为NULL
/* out of memory */
//内存溢出
if (action == HASH_ENTER_NULL)
return NULL;
/* report a generic message */
//报错
if (hashp->isshared)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of shared memory")));
else
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory")));
}
//正常
/* link into hashbucket chain */
//连接到哈希桶链中
*prevBucketPtr = currBucket;
currBucket->link = NULL;
/* copy key into record */
//拷贝键到记录中
currBucket->hashvalue = hashvalue;
hashp->keycopy(ELEMENTKEY(currBucket), keyPtr, keysize);
/*
* Caller is expected to fill the data field on return. DO NOT
* insert any code that could possibly throw error here, as doing
* so would leave the table entry incomplete and hence corrupt the
* caller's data structure.
* 调用者期望在返回时已填充了数据.
* 不要插入有可能抛出异常的代码,因为这样做可能会导致哈希表条目不完整并因此破坏调用者的数据结构
*/
return (void *) ELEMENTKEY(currBucket);
}
//如执行到这里,那程序就有问题了.
elog(ERROR, "unrecognized hash action code: %d", (int) action);
//返回NULL,让编译器shut up
return NULL; /* keep compiler quiet */
//转换hash值为桶号
static inline uint32
calc_bucket(HASHHDR *hctl, uint32 hash_val)
{
uint32 bucket;//桶号
bucket = hash_val & hctl->high_mask;//执行&操作
if (bucket > hctl->max_bucket)//大于最大桶号,则返回low_mask
bucket = bucket & hctl->low_mask;
return bucket;
### 二、跟踪分析
使用gdb跟踪LockAcquireExtended方法
Breakpoint 1 at 0x8cc3f6: file lock.c, line 739.
(gdb) c
Continuing.
reportMemoryError=true, locallockp=0x7ffdd23a8938) at lock.c:739
739 LOCKMETHODID lockmethodid = locktag->locktag_lockmethodid;
(gdb) p
locktag
$1 = {locktag_field1 = 74840, locktag_field2 = 2662, locktag_field3 = 0, locktag_field4 = 0,
locktag_type = 0 ‘\000’, locktag_lockmethodid = 1 ‘\001’}
(gdb) n
750 bool log_lock = false;
(gdb)
752 if (lockmethodid <= 0 || lockmethodid >= lengthof(LockMethods))
(gdb)
754 lockMethodTable = LockMethods[lockmethodid];
(gdb) n
755 if (lockmode <= 0 || lockmode > lockMethodTable->numLockModes)
(gdb)
758 if (RecoveryInProgress() && !InRecovery &&
(gdb)
776 if (sessionLock)
(gdb)
779 owner = CurrentResourceOwner;
(gdb)
784 MemSet(&localtag, 0, sizeof(localtag)); / must clear padding
/
(gdb)
785 localtag.lock = locktag;
(gdb)
786 localtag.mode = lockmode;
(gdb)
788 locallock = (LOCALLOCK
) hash_search(LockMethodLocalHash,
(gdb)
795 if (!found)
(gdb)
797 locallock->lock = NULL;
(gdb)
798 locallock->proclock = NULL;
(gdb)
799 locallock->hashcode = LockTagHashCode(&(localtag.lock));
(gdb)
800 locallock->nLocks = 0;
(gdb)
801 locallock->holdsStrongLockCount = false;
(gdb)
802 locallock->lockCleared = false;
(gdb)
803 locallock->numLockOwners = 0;
(gdb)
804 locallock->maxLockOwners = 8;
(gdb)
805 locallock->lockOwners = NULL; / in case next line fails
/
(gdb)
808 locallock->maxLockOwners sizeof(LOCALLOCKOWNER));
(gdb)
807 MemoryContextAlloc(TopMemoryContext,
(gdb)
806 locallock->lockOwners = (LOCALLOCKOWNER
)
(gdb)
823 hashcode = locallock->hashcode;
(gdb) p locallock
$2 = {tag = {lock = {locktag_field1 = 74840, locktag_field2 = 2662, locktag_field3 = 0, locktag_field4 = 0,
locktag_type = 0 ‘\000’, locktag_lockmethodid = 1 ‘\001’}, mode = 3}, hashcode = 3819421354, lock = 0x0,
proclock = 0x0, nLocks = 0, numLockOwners = 0, maxLockOwners = 8, lockOwners = 0x1c01348,
holdsStrongLockCount = false, lockCleared = false}
(gdb) n
825 if (locallockp)
(gdb)
826
locallockp = locallock;
(gdb)
834 if (locallock->nLocks > 0)
(gdb)
855 if (lockmode >= AccessExclusiveLock &&
(gdb)
874 if (EligibleForRelationFastPath(locktag, lockmode) &&
(gdb)
875 FastPathLocalUseCount < FP_LOCK_SLOTS_PER_BACKEND)
(gdb)
874 if (EligibleForRelationFastPath(locktag, lockmode) &&
(gdb)
877 uint32 fasthashcode = FastPathStrongLockHashPartition(hashcode);
(gdb)
886 LWLockAcquire(&MyProc->backendLock, LW_EXCLUSIVE);
(gdb) p fasthashcode
$3 = 682
(gdb) p MyProc
$4 = {links = {prev = 0x0, next = 0x0}, procgloballist = 0x7fc1b21a4318, sem = 0x7fc190090138, waitStatus = 0,
procLatch = {is_set = 0, is_shared = true, owner_pid = 19985}, lxid = 40047, pid = 19985, pgprocno = 98,
backendId = 2, databaseId = 74840, roleId = 10, tempNamespaceId = 0, isBackgroundWorker = false,
recoveryConflictPending = false, lwWaiting = false, lwWaitMode = 0 ‘\000’, lwWaitLink = {next = 0, prev = 0},
cvWaitLink = {next = 0, prev = 0}, waitLock = 0x0, waitProcLock = 0x0, waitLockMode = 0, heldLocks = 0,
waitLSN = 0, syncRepState = 0, syncRepLinks = {prev = 0x0, next = 0x0}, myProcLocks = {{prev = 0x7fc1b1ed9b30,
next = 0x7fc1b255cea0}, {prev = 0x7fc1b1ede8b0, next = 0x7fc1b255ccc0}, {prev = 0x7fc1b1edda50,
next = 0x7fc1b1ee8220}, {prev = 0x7fc1b1f1c5c0, next = 0x7fc1b1ee2730}, {prev = 0x7fc1b1eddd70,
next = 0x7fc1b1ee2690}, {prev = 0x7fc1b2507400, next = 0x7fc1b1ee2aa0}, {prev = 0x7fc1b1ee2cd0,
next = 0x7fc1b1f311a0}, {prev = 0x7fc1b1f30ed0, next = 0x7fc1b1f31010}, {prev = 0x7fc1b1ee2550,
next = 0x7fc1b1ee8270}, {prev = 0x7fc1b1ee8130, next = 0x7fc1b1f3ab10}, {prev = 0x7fc1b2581ea0,
next = 0x7fc1b1f3a7a0}, {prev = 0x7fc1b2551000, next = 0x7fc1b2550ce0}, {prev = 0x7fc1b1f48da0,
next = 0x7fc1b1f49390}, {prev = 0x7fc1b1f4d5d0, next = 0x7fc1b1f4dad0}, {prev = 0x7fc1b1f09e70,
next = 0x7fc1b255cc20}, {prev = 0x7fc1b1f1cc00, next = 0x7fc1b255cb80}}, subxids = {xids = {
0
procArrayGroupMemberXid = 0, wait_event_info = 0, clogGroupMember = false, clogGroupNext = {
value = 2147483647}, clogGroupMemberXid = 0, clogGroupMemberXidStatus = 0, clogGroupMemberPage = -1,
clogGroupMemberLsn = 0, backendLock = {tranche = 57, state = {value = 536870912}, waiters = {
head = 2147483647, tail = 2147483647}}, fpLockBits = 140737488355328, fpRelId = {2841, 2840, 2655, 2603,
2659, 2658, 2704, 2703, 3164, 3085, 2674, 2674, 2674, 2673, 1247, 1259}, fpVXIDLock = true,
fpLocalTransactionId = 40047, lockGroupLeader = 0x0, lockGroupMembers = {head = {prev = 0x7fc1b21b8b50,
next = 0x7fc1b21b8b50}}, lockGroupLink = {prev = 0x7fc1b21b8b50, next = 0x7fc1b21b8b50}}
(gdb) n
887 if (FastPathStrongRelationLocks->count[fasthashcode] != 0)
(gdb)
888 acquired = false;
(gdb)
892 LWLockRelease(&MyProc->backendLock);
(gdb)
893 if (acquired)
(gdb)
913 if (ConflictsWithRelationFastPath(locktag, lockmode))
(gdb)
941 partitionLock = LockHashPartitionLock(hashcode);
(gdb)
943 LWLockAcquire(partitionLock, LW_EXCLUSIVE);
(gdb)
954 proclock = SetupLockInTable(lockMethodTable, MyProc, locktag,
(gdb)
956 if (!proclock)
(gdb) p *proclock
$5 = {tag = {myLock = 0x7fc1b1e4b210, myProc = 0x7fc1b21b8820}, groupLeader = 0x7fc1b21b8820, holdMask = 0,
releaseMask = 0, lockLink = {prev = 0x7fc1b1e4b228, next = 0x7fc1b1e4b228}, procLink = {prev = 0x7fc1b2581ea0,
next = 0x7fc1b21b8958}}
(gdb) n
972 locallock->proclock = proclock;
(gdb)
973 lock = proclock->tag.myLock;
(gdb)
974 locallock->lock = lock;
(gdb)
981 if (lockMethodTable->conflictTab[lockmode] & lock->waitMask)
(gdb)
984 status = LockCheckConflicts(lockMethodTable, lockmode,
(gdb)
987 if (status == STATUS_OK)
(gdb)
990 GrantLock(lock, proclock, lockmode);
(gdb)
991 GrantLockLocal(locallock, owner);
(gdb)
1086 FinishStrongLockAcquire();
(gdb)
1088 LWLockRelease(partitionLock);
(gdb)
1094 if (log_lock)
(gdb)
1105 return LOCKACQUIRE_OK;
(gdb)
1106 }
(gdb)
使用gdb跟踪,条件是lockmode=8(对应的是AccessExclusiveLock),这时候意味着relation已创建。
pid | mode | locktype | relation
———+——————————-+———————-+—————
1877 | ExclusiveLock | virtualxid |
1877 | ExclusiveLock | transactionid |
1877 | AccessExclusiveLock | relation | 533609
1877 | AccessShareLock | object |
(4 rows)
#
Continuing.
locktag=0x7ffc8e47b950, hashcode=1256016749, lockmode=8) at lock.c:1131
1131 lock = (LOCK *) hash_search_with_hash_value(LockMethodLockHash,
#
Breakpoint 2 at 0x8ccf4e: file lock.c, line 1131.
(gdb) c
Continuing.
locktag=0x7ffc8e47b950, hashcode=1950491801, lockmode=8) at lock.c:1131
1131 lock = (LOCK
) hash_search_with_hash_value(LockMethodLockHash,
(gdb) step
hash_search_with_hash_value (hashp=0x2549160, keyPtr=0x7ffc8e47b950, hashvalue=1950491801,
action=HASH_ENTER_NULL, foundPtr=0x7ffc8e47b7bf) at dynahash.c:925
925 HASHHDR hctl = hashp->hctl;
(gdb) p
hashp
$3 = {hctl = 0x7f4af2da3980, dir = 0x7f4af2da3cd8, hash = 0xa79ac6
keycopy = 0x47d0a0
memcpy@plt, alloc = 0x8c3419
tabname = 0x25491c0 “LOCK hash”, isshared = true, isfixed = false, frozen = false, keysize = 16,
ssize = 256, sshift = 8}
(gdb) p hashp->hash
$4 = {uint32 (const void
, Size)} 0xa79ac6
(gdb) p hashp->hctl
$5 = {freeList = {{mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2db15b8}, {mutex = 0 ‘\000’,
nentries = 0, freeList = 0x7f4af2db6738}, {mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2dbb8b8}, {
mutex = 0 ‘\000’, nentries = 1, freeList = 0x7f4af2dc0990}, {mutex = 0 ‘\000’, nentries = 0,
freeList = 0x7f4af2dc5bb8}, {mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2dcad38}, {
mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2dcfeb8}, {mutex = 0 ‘\000’, nentries = 0,
freeList = 0x7f4af2dd5038}, {mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2dda1b8}, {
mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2ddf338}, {mutex = 0 ‘\000’, nentries = 0,
freeList = 0x7f4af2de44b8}, {mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2de9638}, {
mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2dee7b8}, {mutex = 0 ‘\000’, nentries = 1,
freeList = 0x7f4af2df3890}, {mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2df8ab8}, {
mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2dfdc38}, {mutex = 0 ‘\000’, nentries = 0,
freeList = 0x7f4af2e02db8}, {mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2e07f38}, {
mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2e0d0b8}, {mutex = 0 ‘\000’, nentries = 0,
freeList = 0x7f4af2e12238}, {mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2e173b8}, {
mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2e1c538}, {mutex = 0 ‘\000’, nentries = 0,
freeList = 0x7f4af2e216b8}, {mutex = 0 ‘\000’, nentries = 1, freeList = 0x7f4af2e26790}, {
mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2e2b9b8}, {mutex = 0 ‘\000’, nentries = 0,
freeList = 0x7f4af2e30b38}, {mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2e35cb8}, {
mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2e3ae38}, {mutex = 0 ‘\000’, nentries = 0,
freeList = 0x7f4af2e3ffb8}, {mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2e45138}, {
mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2e4a2b8}, {mutex = 0 ‘\000’, nentries = 0,
freeList = 0x7f4af2e4f438}}, dsize = 256, nsegs = 16, max_bucket = 4095, high_mask = 8191,
low_mask = 4095, keysize = 16, entrysize = 152, num_partitions = 16, ffactor = 1, max_dsize = 256,
ssize = 256, sshift = 8, nelem_alloc = 48}
(gdb) p
hashp->hctl->freeList[3]->freeList
$6 = {link = 0x7f4af2dc08e8, hashvalue = 0}
(gdb) p hashp->hctl->freeList[3]->nentries
Cannot access memory at address 0x1
(gdb) p hashp->hctl->freeList[3]->nentries
$7 = 1
(gdb) p
hashp->hctl->freeList[3]->freeList->link
$8 = {link = 0x7f4af2dc0840, hashvalue = 0}
(gdb) p hashp->hctl->freeList[0]->freeList
$9 = {link = 0x7f4af2db1510, hashvalue = 0}
(gdb) n
926 int freelist_idx = FREELIST_IDX(hctl, hashvalue);
(gdb) p hashvalue
$10 = 1950491801
(gdb) n
949 if (action == HASH_ENTER || action == HASH_ENTER_NULL)
(gdb) p freelist_idx
$11 = 25
(gdb) n
956 if (!IS_PARTITIONED(hctl) && !hashp->frozen &&
(gdb)
965 bucket = calc_bucket(hctl, hashvalue);
(gdb)
967 segment_num = bucket >> hashp->sshift;
(gdb) p bucket
$12 = 1177
(gdb) n
968 segment_ndx = MOD(bucket, hashp->ssize);
(gdb)
970 segp = hashp->dir[segment_num];
(gdb)
972 if (segp == NULL)
(gdb) p segment_num
$13 = 4
(gdb) p segment_ndx
$14 = 153
(gdb) n
975 prevBucketPtr = &segp[segment_ndx];
(gdb)
976 currBucket =
prevBucketPtr;
(gdb)
981 match = hashp->match; / save one fetch in inner loop
/
(gdb)
982 keysize = hashp->keysize; / ditto
/
(gdb)
984 while (currBucket != NULL)
(gdb)
997 if (foundPtr)
(gdb)
998 foundPtr = (bool) (currBucket != NULL);
(gdb)
1003 switch (action)
(gdb)
1042 Assert(hashp->alloc != DynaHashAlloc);
(gdb)
1047 if (currBucket != NULL)
(gdb)
1051 if (hashp->frozen)
(gdb)
1055 currBucket = get_hash_entry(hashp, freelist_idx);
(gdb) step
get_hash_entry (hashp=0x2549160, freelist_idx=25) at dynahash.c:1252
1252 HASHHDR
hctl = hashp->hctl;
(gdb) n
1258 if (IS_PARTITIONED(hctl))
(gdb)
1259 SpinLockAcquire(&hctl->freeList[freelist_idx].mutex);
(gdb)
1262 newElement = hctl->freeList[freelist_idx].freeList;
(gdb) p hctl->freeList[freelist_idx].freeList
$15 = (HASHELEMENT ) 0x7f4af2e30b38
(gdb) p
hctl->freeList[freelist_idx].freeList
$16 = {link = 0x7f4af2e30a90, hashvalue = 3590561593}
(gdb) n
1264 if (newElement != NULL)
(gdb)
1265 break;
(gdb)
1322 hctl->freeList[freelist_idx].freeList = newElement->link;
(gdb)
1323 hctl->freeList[freelist_idx].nentries++;
(gdb)
1325 if (IS_PARTITIONED(hctl))
(gdb)
1326 SpinLockRelease(&hctl->freeList[freelist_idx].mutex);
(gdb) p hctl
$17 = {freeList = {{mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2db15b8}, {mutex = 0 ‘\000’,
nentries = 0, freeList = 0x7f4af2db6738}, {mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2dbb8b8}, {
mutex = 0 ‘\000’, nentries = 1, freeList = 0x7f4af2dc0990}, {mutex = 0 ‘\000’, nentries = 0,
freeList = 0x7f4af2dc5bb8}, {mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2dcad38}, {
mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2dcfeb8}, {mutex = 0 ‘\000’, nentries = 0,
freeList = 0x7f4af2dd5038}, {mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2dda1b8}, {
mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2ddf338}, {mutex = 0 ‘\000’, nentries = 0,
freeList = 0x7f4af2de44b8}, {mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2de9638}, {
mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2dee7b8}, {mutex = 0 ‘\000’, nentries = 1,
freeList = 0x7f4af2df3890}, {mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2df8ab8}, {
mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2dfdc38}, {mutex = 0 ‘\000’, nentries = 0,
freeList = 0x7f4af2e02db8}, {mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2e07f38}, {
mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2e0d0b8}, {mutex = 0 ‘\000’, nentries = 0,
freeList = 0x7f4af2e12238}, {mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2e173b8}, {
mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2e1c538}, {mutex = 0 ‘\000’, nentries = 0,
freeList = 0x7f4af2e216b8}, {mutex = 0 ‘\000’, nentries = 1, freeList = 0x7f4af2e26790}, {
mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2e2b9b8}, {mutex = 1 ‘\001’, nentries = 1,
freeList = 0x7f4af2e30a90}, {mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2e35cb8}, {
mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2e3ae38}, {mutex = 0 ‘\000’, nentries = 0,
freeList = 0x7f4af2e3ffb8}, {mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2e45138}, {
mutex = 0 ‘\000’, nentries = 0, freeList = 0x7f4af2e4a2b8}, {mutex = 0 ‘\000’, nentries = 0,
freeList = 0x7f4af2e4f438}}, dsize = 256, nsegs = 16, max_bucket = 4095, high_mask = 8191,
low_mask = 4095, keysize = 16, entrysize = 152, num_partitions = 16, ffactor = 1, max_dsize = 256,
ssize = 256, sshift = 8, nelem_alloc = 48}
(gdb) n
1328 return newElement;
(gdb)
1329 }
(gdb)
hash_search_with_hash_value (hashp=0x2549160, keyPtr=0x7ffc8e47b950, hashvalue=1950491801,
action=HASH_ENTER_NULL, foundPtr=0x7ffc8e47b7bf) at dynahash.c:1056
1056 if (currBucket == NULL)
(gdb)
1073
prevBucketPtr = currBucket;
(gdb)
1074 currBucket->link = NULL;
(gdb)
1077 currBucket->hashvalue = hashvalue;
(gdb)
1078 hashp->keycopy(ELEMENTKEY(currBucket), keyPtr, keysize);
(gdb)
1087 return (void ) ELEMENTKEY(currBucket);
(gdb)
DONE!
三、参考资料