lwhistogram-v2.patch
application/octet-stream
Filename: lwhistogram-v2.patch
Type: application/octet-stream
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v2
| File | + | − |
|---|---|---|
| src/backend/postmaster/postmaster.c | 2 | 0 |
| src/backend/storage/buffer/bufmgr.c | 3 | 3 |
| src/backend/storage/lmgr/lwlock.c | 136 | 4 |
| src/include/storage/bufmgr.h | 5 | 1 |
| src/include/storage/lwlock.h | 15 | 3 |
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 1dac695..fbe2b78 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -4183,6 +4183,8 @@ SubPostmasterMain(int argc, char *argv[])
static void
ExitPostmaster(int status)
{
+ LWLockDumpHistograms();
+
/* should cleanup shared memory and kill all backends */
/*
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 4de6a72..a20638b 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -2436,7 +2436,7 @@ UnlockBuffers(void)
* Acquire or release the content_lock for the buffer.
*/
void
-LockBuffer(Buffer buffer, int mode)
+LockBufferReally(Buffer buffer, int mode, const char *file, int line)
{
volatile BufferDesc *buf;
@@ -2449,9 +2449,9 @@ LockBuffer(Buffer buffer, int mode)
if (mode == BUFFER_LOCK_UNLOCK)
LWLockRelease(buf->content_lock);
else if (mode == BUFFER_LOCK_SHARE)
- LWLockAcquire(buf->content_lock, LW_SHARED);
+ LWLockAcquireReally(buf->content_lock, LW_SHARED, file, line);
else if (mode == BUFFER_LOCK_EXCLUSIVE)
- LWLockAcquire(buf->content_lock, LW_EXCLUSIVE);
+ LWLockAcquireReally(buf->content_lock, LW_EXCLUSIVE, file, line);
else
elog(ERROR, "unrecognized buffer lock mode: %d", mode);
}
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index f152376..a469d8b 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -27,6 +27,7 @@
#include "commands/async.h"
#include "miscadmin.h"
#include "pg_trace.h"
+#include "portability/instr_time.h"
#include "storage/ipc.h"
#include "storage/predicate.h"
#include "storage/proc.h"
@@ -45,6 +46,9 @@ typedef struct LWLock
int shared; /* # of shared holders (0..MaxBackends) */
PGPROC *head; /* head of list of waiting PGPROCs */
PGPROC *tail; /* tail of list of waiting PGPROCs */
+ const char *file; /* file of lock holder */
+ int line; /* line of lock holder */
+ uint64 histogram[33]; /* gigantic! */
/* tail is undefined when head is NULL */
} LWLock;
@@ -60,7 +64,7 @@ typedef struct LWLock
* LWLock is between 16 and 32 bytes on all known platforms, so these two
* cases are sufficient.
*/
-#define LWLOCK_PADDED_SIZE (sizeof(LWLock) <= 16 ? 16 : 32)
+#define LWLOCK_PADDED_SIZE 512
typedef union LWLockPadded
{
@@ -84,6 +88,8 @@ NON_EXEC_STATIC LWLockPadded *LWLockArray = NULL;
*/
#define MAX_SIMUL_LWLOCKS 100
+#define LWLOCK_GRIPE_TIME 1000
+
static int num_held_lwlocks = 0;
static LWLockId held_lwlocks[MAX_SIMUL_LWLOCKS];
@@ -336,14 +342,20 @@ LWLockAssign(void)
* Side effect: cancel/die interrupts are held off until lock release.
*/
void
-LWLockAcquire(LWLockId lockid, LWLockMode mode)
+LWLockAcquireReally(LWLockId lockid, LWLockMode mode, const char *file,
+ int line)
{
volatile LWLock *lock = &(LWLockArray[lockid].lock);
PGPROC *proc = MyProc;
bool retry = false;
int extraWaits = 0;
+ instr_time start_time;
+ const char *blocked_by_file = "_fail_";
+ int blocked_by_line = 0;
+ int64 time_elapsed = 0;
PRINT_LWDEBUG("LWLockAcquire", lockid, lock);
+ INSTR_TIME_SET_ZERO(start_time);
#ifdef LWLOCK_STATS
/* Set up local count state first time through in a given process */
@@ -407,20 +419,33 @@ LWLockAcquire(LWLockId lockid, LWLockMode mode)
if (lock->exclusive == 0 && lock->shared == 0)
{
lock->exclusive++;
+ lock->file = file;
+ lock->line = line;
mustwait = false;
}
else
+ {
mustwait = true;
+ blocked_by_file = lock->file;
+ blocked_by_line = lock->line;
+ }
}
else
{
if (lock->exclusive == 0)
{
lock->shared++;
+ lock->file = file;
+ lock->line = line;
mustwait = false;
}
else
+ {
mustwait = true;
+ blocked_by_file = lock->file;
+ blocked_by_line = lock->line;
+ }
+
}
if (!mustwait)
@@ -462,6 +487,9 @@ LWLockAcquire(LWLockId lockid, LWLockMode mode)
*/
LOG_LWDEBUG("LWLockAcquire", lockid, "waiting");
+ if (INSTR_TIME_IS_ZERO(start_time))
+ INSTR_TIME_SET_CURRENT(start_time);
+
#ifdef LWLOCK_STATS
block_counts[lockid]++;
#endif
@@ -485,9 +513,29 @@ LWLockAcquire(LWLockId lockid, LWLockMode mode)
retry = true;
}
+ if (!INSTR_TIME_IS_ZERO(start_time))
+ {
+ instr_time end_time;
+ int f;
+
+ INSTR_TIME_SET_CURRENT(end_time);
+ INSTR_TIME_SUBTRACT(end_time, start_time);
+ time_elapsed = INSTR_TIME_GET_MICROSEC(end_time);
+ f = fls((int) time_elapsed);
+ lock->histogram[f]++;
+ }
+
/* We are done updating shared state of the lock itself. */
SpinLockRelease(&lock->mutex);
+ if (!INSTR_TIME_IS_ZERO(start_time))
+ {
+ if (time_elapsed >= LWLOCK_GRIPE_TIME)
+ elog(LOG, "lock %d: waited %.3f ms at %s:%d blocked by %s:%d",
+ lockid, ((double) time_elapsed) / 1000.0,
+ file, line, blocked_by_file, blocked_by_line);
+ }
+
TRACE_POSTGRESQL_LWLOCK_ACQUIRE(lockid, mode);
/* Add lock to list of locks held by this backend */
@@ -508,10 +556,13 @@ LWLockAcquire(LWLockId lockid, LWLockMode mode)
* If successful, cancel/die interrupts are held off until lock release.
*/
bool
-LWLockConditionalAcquire(LWLockId lockid, LWLockMode mode)
+LWLockConditionalAcquireReally(LWLockId lockid, LWLockMode mode,
+ const char *file, int line)
{
volatile LWLock *lock = &(LWLockArray[lockid].lock);
bool mustwait;
+ const char *blocked_by_file;
+ int blocked_by_line;
PRINT_LWDEBUG("LWLockConditionalAcquire", lockid, lock);
@@ -535,20 +586,32 @@ LWLockConditionalAcquire(LWLockId lockid, LWLockMode mode)
if (lock->exclusive == 0 && lock->shared == 0)
{
lock->exclusive++;
+ lock->file = file;
+ lock->line = line;
mustwait = false;
}
else
+ {
mustwait = true;
+ blocked_by_file = lock->file;
+ blocked_by_line = lock->line;
+ }
}
else
{
if (lock->exclusive == 0)
{
lock->shared++;
+ lock->file = file;
+ lock->line = line;
mustwait = false;
}
else
+ {
mustwait = true;
+ blocked_by_file = lock->file;
+ blocked_by_line = lock->line;
+ }
}
/* We are done updating shared state of the lock itself. */
@@ -586,12 +649,15 @@ LWLockConditionalAcquire(LWLockId lockid, LWLockMode mode)
* wake up, observe that their records have already been flushed, and return.
*/
bool
-LWLockAcquireOrWait(LWLockId lockid, LWLockMode mode)
+LWLockAcquireOrWaitReally(LWLockId lockid, LWLockMode mode,
+ const char *file, int line)
{
volatile LWLock *lock = &(LWLockArray[lockid].lock);
PGPROC *proc = MyProc;
bool mustwait;
int extraWaits = 0;
+ const char *blocked_by_file;
+ int blocked_by_line;
PRINT_LWDEBUG("LWLockAcquireOrWait", lockid, lock);
@@ -621,24 +687,41 @@ LWLockAcquireOrWait(LWLockId lockid, LWLockMode mode)
if (lock->exclusive == 0 && lock->shared == 0)
{
lock->exclusive++;
+ lock->file = file;
+ lock->line = line;
mustwait = false;
}
else
+ {
mustwait = true;
+ blocked_by_file = lock->file;
+ blocked_by_line = lock->line;
+ }
}
else
{
if (lock->exclusive == 0)
{
lock->shared++;
+ lock->file = file;
+ lock->line = line;
mustwait = false;
}
else
+ {
mustwait = true;
+ blocked_by_file = lock->file;
+ blocked_by_line = lock->line;
+ }
}
if (mustwait)
{
+ instr_time start_time;
+ instr_time end_time;
+ int64 time_elapsed;
+ int f;
+
/*
* Add myself to wait queue.
*
@@ -667,6 +750,8 @@ LWLockAcquireOrWait(LWLockId lockid, LWLockMode mode)
*/
LOG_LWDEBUG("LWLockAcquireOrWait", lockid, "waiting");
+ INSTR_TIME_SET_CURRENT(start_time);
+
#ifdef LWLOCK_STATS
block_counts[lockid]++;
#endif
@@ -682,6 +767,19 @@ LWLockAcquireOrWait(LWLockId lockid, LWLockMode mode)
extraWaits++;
}
+ INSTR_TIME_SET_CURRENT(end_time);
+ INSTR_TIME_SUBTRACT(end_time, start_time);
+ time_elapsed = INSTR_TIME_GET_MICROSEC(end_time);
+ f = fls((int) time_elapsed);
+ SpinLockAcquire(&lock->mutex);
+ lock->histogram[f]++;
+ SpinLockRelease(&lock->mutex);
+
+ if (time_elapsed >= LWLOCK_GRIPE_TIME)
+ elog(LOG, "lock %d: waited %.3f ms at %s:%d blocked by %s:%d",
+ lockid, ((double) time_elapsed) / 1000.0,
+ file, line, blocked_by_file, blocked_by_line);
+
TRACE_POSTGRESQL_LWLOCK_WAIT_DONE(lockid, mode);
LOG_LWDEBUG("LWLockAcquireOrWait", lockid, "awakened");
@@ -878,3 +976,37 @@ LWLockHeldByMe(LWLockId lockid)
}
return false;
}
+
+void
+LWLockDumpHistograms(void)
+{
+ int i;
+ int k;
+ char buffer[2048];
+ char *s;
+ int *LWLockCounter = (int *) ((char *) LWLockArray - 2 * sizeof(int));
+ int numLocks = LWLockCounter[1];
+
+ for (i = 0; i < numLocks; i++)
+ {
+ volatile LWLock *lock = &(LWLockArray[i].lock);
+ buffer[0] = '\0';
+ s = buffer;
+
+ for (k = 0; k < 32; ++k)
+ {
+ if (!lock->histogram[k])
+ continue;
+ if (s != buffer)
+ {
+ *s++ = ',';
+ *s++ = ' ';
+ }
+ sprintf(s, "%d:%llu", k,
+ (long long unsigned) lock->histogram[k]);
+ s += strlen(s);
+ }
+ if (s != buffer)
+ elog(LOG, "lock %d: %s", i, buffer);
+ }
+}
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index d72bad9..21f1fa8 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -204,8 +204,12 @@ extern void BufferGetTag(Buffer buffer, RelFileNode *rnode,
extern void SetBufferCommitInfoNeedsSave(Buffer buffer);
+#define LockBuffer(b, m) \
+ LockBufferReally(b, m, __FILE__, __LINE__)
+
extern void UnlockBuffers(void);
-extern void LockBuffer(Buffer buffer, int mode);
+extern void LockBufferReally(Buffer buffer, int mode, const char *file,
+ int line);
extern bool ConditionalLockBuffer(Buffer buffer);
extern void LockBufferForCleanup(Buffer buffer);
extern bool ConditionalLockBufferForCleanup(Buffer buffer);
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 57a4f66..db9dfa5 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -106,9 +106,20 @@ extern bool Trace_lwlocks;
#endif
extern LWLockId LWLockAssign(void);
-extern void LWLockAcquire(LWLockId lockid, LWLockMode mode);
-extern bool LWLockConditionalAcquire(LWLockId lockid, LWLockMode mode);
-extern bool LWLockAcquireOrWait(LWLockId lockid, LWLockMode mode);
+
+#define LWLockAcquire(l, m) \
+ LWLockAcquireReally(l, m, __FILE__, __LINE__)
+#define LWLockConditionalAcquire(l, m) \
+ LWLockConditionalAcquireReally(l, m, __FILE__, __LINE__)
+#define LWLockAcquireOrWait(l, m) \
+ LWLockAcquireOrWaitReally(l, m, __FILE__, __LINE__)
+
+extern void LWLockAcquireReally(LWLockId lockid, LWLockMode mode,
+ const char *file, int line);
+extern bool LWLockConditionalAcquireReally(LWLockId lockid, LWLockMode mode,
+ const char *file, int line);
+extern bool LWLockAcquireOrWaitReally(LWLockId lockid, LWLockMode mode,
+ const char *file, int line);
extern void LWLockRelease(LWLockId lockid);
extern void LWLockReleaseAll(void);
extern bool LWLockHeldByMe(LWLockId lockid);
@@ -118,5 +129,6 @@ extern Size LWLockShmemSize(void);
extern void CreateLWLocks(void);
extern void RequestAddinLWLocks(int n);
+extern void LWLockDumpHistograms(void);
#endif /* LWLOCK_H */