Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 9f7123d

Browse files
committedOct 20, 2015
Add pg_stat_wait extension
1 parent 56dcbf7 commit 9f7123d

15 files changed

+1730
-159
lines changed
 

‎contrib/pg_stat_wait/Makefile

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# contrib/pg_stat_wait/Makefile
2+
3+
MODULE_big = pg_stat_wait
4+
OBJS = pg_stat_wait.o collector.o
5+
6+
EXTENSION = pg_stat_wait
7+
DATA = pg_stat_wait--1.0.sql
8+
PG_CPPFLAGS = -DPG_STAT_WAIT_TESTS
9+
EXTRA_CLEAN = $(pg_regress_clean_files) ./regression_output ./isolation_output
10+
EXTRA_INSTALL=contrib/pg_stat_wait
11+
12+
ifdef USE_PGXS
13+
PG_CONFIG = pg_config
14+
PGXS := $(shell $(PG_CONFIG) --pgxs)
15+
include $(PGXS)
16+
else
17+
subdir = contrib/pg_stat_wait
18+
top_builddir = ../..
19+
include $(top_builddir)/src/Makefile.global
20+
include $(top_srcdir)/contrib/contrib-global.mk
21+
endif
22+
23+
check: regresscheck isolationcheck
24+
25+
submake-regress:
26+
$(MAKE) -C $(top_builddir)/src/test/regress all
27+
28+
submake-isolation:
29+
$(MAKE) -C $(top_builddir)/src/test/isolation all
30+
31+
submake-pg_stat_wait:
32+
$(MAKE) -C $(top_builddir)/contrib/pg_stat_wait
33+
34+
REGRESSCHECKS=file_trace descriptions
35+
36+
regresscheck: all | submake-regress submake-pg_stat_wait
37+
$(MKDIR_P) regression_output
38+
$(pg_regress_check) \
39+
--temp-instance=./tmp_check \
40+
--load-extension=pg_stat_wait \
41+
--outputdir=./regression_output \
42+
$(REGRESSCHECKS)
43+
44+
ISOLATIONCHECKS=history
45+
46+
isolationcheck: all | submake-isolation submake-pg_stat_wait
47+
$(MKDIR_P) isolation_output
48+
$(pg_isolation_regress_check) \
49+
--temp-instance=./tmp_check \
50+
--temp-config=./waits.conf \
51+
--load-extension=pg_stat_wait \
52+
--outputdir=./isolation_output \
53+
$(ISOLATIONCHECKS)
54+

‎contrib/pg_stat_wait/collector.c

+290
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
#include "postgres.h"
2+
3+
#include "access/htup_details.h"
4+
#include "catalog/pg_type.h"
5+
#include "funcapi.h"
6+
#include "miscadmin.h"
7+
#include "postmaster/bgworker.h"
8+
#include "storage/ipc.h"
9+
#include "storage/procarray.h"
10+
#include "storage/procsignal.h"
11+
#include "storage/s_lock.h"
12+
#include "storage/shm_mq.h"
13+
#include "storage/shm_toc.h"
14+
#include "storage/spin.h"
15+
#include "storage/wait.h"
16+
#include "utils/guc.h"
17+
#include "utils/memutils.h"
18+
#include "utils/resowner.h"
19+
#include "portability/instr_time.h"
20+
21+
#include "pg_stat_wait.h"
22+
23+
CollectorShmqHeader *hdr;
24+
25+
static void *pgsw;
26+
shm_toc *toc;
27+
shm_mq *mq;
28+
static volatile sig_atomic_t shutdown_requested = false;
29+
30+
static void handle_sigterm(SIGNAL_ARGS);
31+
static void collector_main(Datum main_arg);
32+
33+
/*
34+
* Estimate shared memory space needed.
35+
*/
36+
Size
37+
CollectorShmemSize(void)
38+
{
39+
shm_toc_estimator e;
40+
Size size;
41+
42+
shm_toc_initialize_estimator(&e);
43+
shm_toc_estimate_chunk(&e, sizeof(CollectorShmqHeader));
44+
shm_toc_estimate_chunk(&e, (Size) COLLECTOR_QUEUE_SIZE);
45+
shm_toc_estimate_keys(&e, 2);
46+
size = shm_toc_estimate(&e);
47+
48+
return size;
49+
}
50+
51+
void
52+
AllocateCollectorMem(void)
53+
{
54+
bool found;
55+
Size segsize= CollectorShmemSize();
56+
57+
pgsw = ShmemInitStruct("pg_stat_wait", segsize, &found);
58+
59+
if (!found)
60+
{
61+
void *mq_mem;
62+
63+
toc = shm_toc_create(PG_STAT_WAIT_MAGIC, pgsw, segsize);
64+
hdr = shm_toc_allocate(toc, sizeof(CollectorShmqHeader));
65+
shm_toc_insert(toc, 0, hdr);
66+
67+
mq_mem = shm_toc_allocate(toc, COLLECTOR_QUEUE_SIZE);
68+
shm_toc_insert(toc, 1, mq_mem);
69+
70+
DefineCustomIntVariable("pg_stat_wait.history_size",
71+
"Sets size of waits history.", NULL,
72+
&hdr->historySize, 5000, 100, INT_MAX,
73+
PGC_SUSET, 0, NULL, NULL, NULL);
74+
75+
DefineCustomIntVariable("pg_stat_wait.history_period",
76+
"Sets period of waits history sampling.", NULL,
77+
&hdr->historyPeriod, 10, 1, INT_MAX,
78+
PGC_SUSET, 0, NULL, NULL, NULL);
79+
80+
DefineCustomBoolVariable("pg_stat_wait.history_skip_latch",
81+
"Skip latch events in waits history", NULL,
82+
&hdr->historySkipLatch, false, PGC_SUSET, 0, NULL, NULL, NULL);
83+
}
84+
else
85+
{
86+
toc = shm_toc_attach(PG_STAT_WAIT_MAGIC, pgsw);
87+
hdr = shm_toc_lookup(toc, 0);
88+
}
89+
}
90+
91+
void
92+
RegisterWaitsCollector(void)
93+
{
94+
BackgroundWorker worker;
95+
96+
/* set up common data for all our workers */
97+
worker.bgw_flags = BGWORKER_SHMEM_ACCESS |
98+
BGWORKER_BACKEND_DATABASE_CONNECTION;
99+
worker.bgw_start_time = BgWorkerStart_RecoveryFinished;
100+
worker.bgw_restart_time = BGW_NEVER_RESTART;
101+
worker.bgw_main = collector_main;
102+
worker.bgw_notify_pid = 0;
103+
snprintf(worker.bgw_name, BGW_MAXLEN, "pg_stat_wait collector");
104+
worker.bgw_main_arg = (Datum)0;
105+
RegisterBackgroundWorker(&worker);
106+
}
107+
108+
void
109+
AllocHistory(History *observations, int count)
110+
{
111+
observations->items = (HistoryItem *) palloc0(sizeof(HistoryItem) * count);
112+
observations->index = 0;
113+
observations->count = count;
114+
observations->wraparound = false;
115+
}
116+
117+
/* Read current wait information from proc, if readCurrent is true,
118+
* then it reads from currently going wait, and can be inconsistent
119+
*/
120+
int
121+
GetCurrentWaitsState(PGPROC *proc, HistoryItem *item, int idx)
122+
{
123+
instr_time currentTime;
124+
ProcWait *wait;
125+
126+
if (idx == -1)
127+
return 0;
128+
129+
INSTR_TIME_SET_CURRENT(currentTime);
130+
wait = &proc->waits.waitsBuf[idx];
131+
item->backendPid = proc->pid;
132+
item->classId = (int)wait->classId;
133+
if (item->classId == 0)
134+
return 0;
135+
136+
item->eventId = (int)wait->eventId;
137+
138+
INSTR_TIME_SUBTRACT(currentTime, wait->startTime);
139+
item->waitTime = INSTR_TIME_GET_MICROSEC(currentTime);
140+
memcpy(item->params, wait->params, sizeof(item->params));
141+
return 1;
142+
}
143+
144+
static void
145+
handle_sigterm(SIGNAL_ARGS)
146+
{
147+
int save_errno = errno;
148+
shutdown_requested = true;
149+
if (MyProc)
150+
SetLatch(&MyProc->procLatch);
151+
errno = save_errno;
152+
}
153+
154+
/* Circulation in history */
155+
static HistoryItem *
156+
get_next_observation(History *observations)
157+
{
158+
HistoryItem *result;
159+
160+
result = &observations->items[observations->index];
161+
observations->index++;
162+
if (observations->index >= observations->count)
163+
{
164+
observations->index = 0;
165+
observations->wraparound = true;
166+
}
167+
return result;
168+
}
169+
170+
/* Gets current waits from backends */
171+
static void
172+
write_waits_history(History *observations, TimestampTz current_ts)
173+
{
174+
int i;
175+
176+
LWLockAcquire(ProcArrayLock, LW_SHARED);
177+
for (i = 0; i < ProcGlobal->allProcCount; ++i)
178+
{
179+
HistoryItem item, *observation;
180+
PGPROC *proc = &ProcGlobal->allProcs[i];
181+
int stateOk = GetCurrentWaitsState(proc, &item, proc->waits.readIdx);
182+
183+
/* mark waits as read */
184+
proc->waits.readIdx = -1;
185+
186+
if (stateOk)
187+
{
188+
if (hdr->historySkipLatch && item.classId == WAIT_LATCH)
189+
continue;
190+
191+
item.ts = current_ts;
192+
observation = get_next_observation(observations);
193+
*observation = item;
194+
}
195+
}
196+
LWLockRelease(ProcArrayLock);
197+
}
198+
199+
static void
200+
send_history(History *observations, shm_mq_handle *mqh)
201+
{
202+
int count, i;
203+
204+
if (observations->wraparound)
205+
count = observations->count;
206+
else
207+
count = observations->index;
208+
209+
shm_mq_send(mqh, sizeof(count), &count, false);
210+
for (i = 0; i < count; i++)
211+
shm_mq_send(mqh, sizeof(HistoryItem), &observations->items[i], false);
212+
}
213+
214+
static void
215+
collector_main(Datum main_arg)
216+
{
217+
shm_mq *mq;
218+
shm_mq_handle *mqh;
219+
History observations;
220+
MemoryContext old_context, collector_context;
221+
222+
/*
223+
* Establish signal handlers.
224+
*
225+
* We want CHECK_FOR_INTERRUPTS() to kill off this worker process just as
226+
* it would a normal user backend. To make that happen, we establish a
227+
* signal handler that is a stripped-down version of die(). We don't have
228+
* any equivalent of the backend's command-read loop, where interrupts can
229+
* be processed immediately, so make sure ImmediateInterruptOK is turned
230+
* off.
231+
*/
232+
pqsignal(SIGTERM, handle_sigterm);
233+
BackgroundWorkerUnblockSignals();
234+
235+
hdr->latch = &MyProc->procLatch;
236+
CurrentResourceOwner = ResourceOwnerCreate(NULL, "pg_stat_wait collector");
237+
collector_context = AllocSetContextCreate(TopMemoryContext,
238+
"pg_stat_wait context",
239+
ALLOCSET_DEFAULT_MINSIZE,
240+
ALLOCSET_DEFAULT_INITSIZE,
241+
ALLOCSET_DEFAULT_MAXSIZE);
242+
old_context = MemoryContextSwitchTo(collector_context);
243+
AllocHistory(&observations, hdr->historySize);
244+
MemoryContextSwitchTo(old_context);
245+
246+
while (1)
247+
{
248+
int rc;
249+
TimestampTz current_ts;
250+
251+
ResetLatch(&MyProc->procLatch);
252+
current_ts = GetCurrentTimestamp();
253+
write_waits_history(&observations, current_ts);
254+
255+
if (shutdown_requested)
256+
break;
257+
258+
rc = WaitLatch(&MyProc->procLatch,
259+
WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
260+
hdr->historyPeriod);
261+
262+
if (rc & WL_POSTMASTER_DEATH)
263+
exit(1);
264+
265+
if (hdr->request == HISTORY_REQUEST)
266+
{
267+
hdr->request = NO_REQUEST;
268+
269+
mq = (shm_mq *)shm_toc_lookup(toc, 1);
270+
shm_mq_set_sender(mq, MyProc);
271+
mqh = shm_mq_attach(mq, NULL, NULL);
272+
shm_mq_wait_for_attach(mqh);
273+
274+
if (shm_mq_get_receiver(mq) != NULL)
275+
send_history(&observations, mqh);
276+
277+
shm_mq_detach(mq);
278+
}
279+
}
280+
281+
MemoryContextReset(collector_context);
282+
283+
/*
284+
* We're done. Explicitly detach the shared memory segment so that we
285+
* don't get a resource leak warning at commit time. This will fire any
286+
* on_dsm_detach callbacks we've registered, as well. Once that's done,
287+
* we can go ahead and exit.
288+
*/
289+
proc_exit(0);
290+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
SELECT * FROM pg_wait_event;
2+
class_id | event_id | name
3+
----------+----------+-----------------------------------
4+
0 | 0 |
5+
1 | 0 |
6+
1 | 1 | ShmemIndexLock
7+
1 | 2 | OidGenLock
8+
1 | 3 | XidGenLock
9+
1 | 4 | ProcArrayLock
10+
1 | 5 | SInvalReadLock
11+
1 | 6 | SInvalWriteLock
12+
1 | 7 | WALBufMappingLock
13+
1 | 8 | WALWriteLock
14+
1 | 9 | ControlFileLock
15+
1 | 10 | CheckpointLock
16+
1 | 11 | CLogControlLock
17+
1 | 12 | SubtransControlLock
18+
1 | 13 | MultiXactGenLock
19+
1 | 14 | MultiXactOffsetControlLock
20+
1 | 15 | MultiXactMemberControlLock
21+
1 | 16 | RelCacheInitLock
22+
1 | 17 | CheckpointerCommLock
23+
1 | 18 | TwoPhaseStateLock
24+
1 | 19 | TablespaceCreateLock
25+
1 | 20 | BtreeVacuumLock
26+
1 | 21 | AddinShmemInitLock
27+
1 | 22 | AutovacuumLock
28+
1 | 23 | AutovacuumScheduleLock
29+
1 | 24 | SyncScanLock
30+
1 | 25 | RelationMappingLock
31+
1 | 26 | AsyncCtlLock
32+
1 | 27 | AsyncQueueLock
33+
1 | 28 | SerializableXactHashLock
34+
1 | 29 | SerializableFinishedListLock
35+
1 | 30 | SerializablePredicateLockListLock
36+
1 | 31 | OldSerXidLock
37+
1 | 32 | SyncRepLock
38+
1 | 33 | BackgroundWorkerLock
39+
1 | 34 | DynamicSharedMemoryControlLock
40+
1 | 35 | AutoFileLock
41+
1 | 36 | ReplicationSlotAllocationLock
42+
1 | 37 | ReplicationSlotControlLock
43+
1 | 38 | CommitTsControlLock
44+
1 | 39 | CommitTsLock
45+
1 | 40 | ReplicationOriginLock
46+
1 | 41 | UserDefinedLocks
47+
1 | 42 | WALInsertLocks
48+
1 | 43 | CLogBufferLocks
49+
1 | 44 | CommitTSBufferLocks
50+
1 | 45 | SubtransBufferLocks
51+
1 | 46 | MultiXactOffsetBufferLocks
52+
1 | 47 | MultiXactMemberBufferLocks
53+
1 | 48 | BufferMgrLocks
54+
1 | 49 | BufferLWLocks
55+
1 | 50 | LockMgrLWLocks
56+
1 | 51 | OldSerXidBufferLocks
57+
1 | 52 | PredicateLWLocks
58+
1 | 53 | ProcessLocks
59+
1 | 54 | AsyncBufferLocks
60+
2 | 0 | Relation
61+
2 | 1 | RelationExtend
62+
2 | 2 | Page
63+
2 | 3 | Tuple
64+
2 | 4 | Transaction
65+
2 | 5 | VirtualTransaction
66+
2 | 6 | SpeculativeToken
67+
2 | 7 | Object
68+
2 | 8 | Userlock
69+
2 | 9 | Advisory
70+
3 | 0 | SMGR_READ
71+
3 | 1 | SMGR_WRITE
72+
3 | 2 | SMGR_FSYNC
73+
3 | 3 | XLOG_READ
74+
3 | 4 | XLOG_WRITE
75+
3 | 5 | XLOG_FSYNC
76+
3 | 6 | SLRU_READ
77+
3 | 7 | SLRU_WRITE
78+
3 | 8 | SLRU_FSYNC
79+
4 | 0 | Latch
80+
5 | 0 | READ
81+
5 | 1 | WRITE
82+
5 | 2 | SYSLOG
83+
6 | 0 | Allocations
84+
(80 rows)
85+
86+
SELECT * FROM pg_wait_class;
87+
class_id | name
88+
----------+-------------
89+
1 | LWLocks
90+
2 | Locks
91+
3 | Storage
92+
4 | Latch
93+
5 | Network
94+
6 | Allocations
95+
(6 rows)
96+
97+
SELECT * FROM pg_wait_events;
98+
class_id | class_name | event_id | event_name
99+
----------+-------------+----------+-----------------------------------
100+
1 | LWLocks | 0 |
101+
1 | LWLocks | 1 | ShmemIndexLock
102+
1 | LWLocks | 2 | OidGenLock
103+
1 | LWLocks | 3 | XidGenLock
104+
1 | LWLocks | 4 | ProcArrayLock
105+
1 | LWLocks | 5 | SInvalReadLock
106+
1 | LWLocks | 6 | SInvalWriteLock
107+
1 | LWLocks | 7 | WALBufMappingLock
108+
1 | LWLocks | 8 | WALWriteLock
109+
1 | LWLocks | 9 | ControlFileLock
110+
1 | LWLocks | 10 | CheckpointLock
111+
1 | LWLocks | 11 | CLogControlLock
112+
1 | LWLocks | 12 | SubtransControlLock
113+
1 | LWLocks | 13 | MultiXactGenLock
114+
1 | LWLocks | 14 | MultiXactOffsetControlLock
115+
1 | LWLocks | 15 | MultiXactMemberControlLock
116+
1 | LWLocks | 16 | RelCacheInitLock
117+
1 | LWLocks | 17 | CheckpointerCommLock
118+
1 | LWLocks | 18 | TwoPhaseStateLock
119+
1 | LWLocks | 19 | TablespaceCreateLock
120+
1 | LWLocks | 20 | BtreeVacuumLock
121+
1 | LWLocks | 21 | AddinShmemInitLock
122+
1 | LWLocks | 22 | AutovacuumLock
123+
1 | LWLocks | 23 | AutovacuumScheduleLock
124+
1 | LWLocks | 24 | SyncScanLock
125+
1 | LWLocks | 25 | RelationMappingLock
126+
1 | LWLocks | 26 | AsyncCtlLock
127+
1 | LWLocks | 27 | AsyncQueueLock
128+
1 | LWLocks | 28 | SerializableXactHashLock
129+
1 | LWLocks | 29 | SerializableFinishedListLock
130+
1 | LWLocks | 30 | SerializablePredicateLockListLock
131+
1 | LWLocks | 31 | OldSerXidLock
132+
1 | LWLocks | 32 | SyncRepLock
133+
1 | LWLocks | 33 | BackgroundWorkerLock
134+
1 | LWLocks | 34 | DynamicSharedMemoryControlLock
135+
1 | LWLocks | 35 | AutoFileLock
136+
1 | LWLocks | 36 | ReplicationSlotAllocationLock
137+
1 | LWLocks | 37 | ReplicationSlotControlLock
138+
1 | LWLocks | 38 | CommitTsControlLock
139+
1 | LWLocks | 39 | CommitTsLock
140+
1 | LWLocks | 40 | ReplicationOriginLock
141+
1 | LWLocks | 41 | UserDefinedLocks
142+
1 | LWLocks | 42 | WALInsertLocks
143+
1 | LWLocks | 43 | CLogBufferLocks
144+
1 | LWLocks | 44 | CommitTSBufferLocks
145+
1 | LWLocks | 45 | SubtransBufferLocks
146+
1 | LWLocks | 46 | MultiXactOffsetBufferLocks
147+
1 | LWLocks | 47 | MultiXactMemberBufferLocks
148+
1 | LWLocks | 48 | BufferMgrLocks
149+
1 | LWLocks | 49 | BufferLWLocks
150+
1 | LWLocks | 50 | LockMgrLWLocks
151+
1 | LWLocks | 51 | OldSerXidBufferLocks
152+
1 | LWLocks | 52 | PredicateLWLocks
153+
1 | LWLocks | 53 | ProcessLocks
154+
1 | LWLocks | 54 | AsyncBufferLocks
155+
2 | Locks | 0 | Relation
156+
2 | Locks | 1 | RelationExtend
157+
2 | Locks | 2 | Page
158+
2 | Locks | 3 | Tuple
159+
2 | Locks | 4 | Transaction
160+
2 | Locks | 5 | VirtualTransaction
161+
2 | Locks | 6 | SpeculativeToken
162+
2 | Locks | 7 | Object
163+
2 | Locks | 8 | Userlock
164+
2 | Locks | 9 | Advisory
165+
3 | Storage | 0 | SMGR_READ
166+
3 | Storage | 1 | SMGR_WRITE
167+
3 | Storage | 2 | SMGR_FSYNC
168+
3 | Storage | 3 | XLOG_READ
169+
3 | Storage | 4 | XLOG_WRITE
170+
3 | Storage | 5 | XLOG_FSYNC
171+
3 | Storage | 6 | SLRU_READ
172+
3 | Storage | 7 | SLRU_WRITE
173+
3 | Storage | 8 | SLRU_FSYNC
174+
4 | Latch | 0 | Latch
175+
5 | Network | 0 | READ
176+
5 | Network | 1 | WRITE
177+
5 | Network | 2 | SYSLOG
178+
6 | Allocations | 0 | Allocations
179+
(79 rows)
180+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
select pg_start_trace(0, '/tmp/pg_stat_wait.trace');
2+
pg_start_trace
3+
----------------
4+
5+
(1 row)
6+
7+
select pg_is_in_trace(0);
8+
pg_is_in_trace
9+
----------------
10+
t
11+
(1 row)
12+
13+
select pg_stop_trace(0);
14+
pg_stop_trace
15+
---------------
16+
17+
(1 row)
18+
+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
Parsed test spec with 2 sessions
2+
3+
starting permutation: start_lwlock get_current_lwlock stop_wait get_profile get_history_lwlock
4+
step start_lwlock:
5+
SELECT pg_stat_wait_start_wait(1, 10, 1, 0, 0, 0, 5);
6+
SELECT pg_sleep(1);
7+
8+
pg_stat_wait_start_wait
9+
10+
11+
pg_sleep
12+
13+
14+
step get_current_lwlock:
15+
SELECT
16+
pid > 0,
17+
(now() - sample_ts) < interval '1 hour',
18+
class_id,
19+
class_name,
20+
event_id,
21+
event_name,
22+
p1, p2, p3, p4, p5
23+
FROM pg_stat_wait_current
24+
WHERE class_id = 1;
25+
26+
?column? ?column? class_id class_name event_id event_name p1 p2 p3 p4 p5
27+
28+
t t 1 LWLocks 10 CheckpointLock 1 0 0 0 5
29+
step stop_wait:
30+
SELECT pg_stat_wait_stop_wait();
31+
32+
pg_stat_wait_stop_wait
33+
34+
35+
step get_profile:
36+
SELECT pid > 0, class_id, class_name, event_id, event_name, wait_time > 0
37+
FROM pg_stat_wait_profile
38+
where class_id = 1 AND event_id = 10 AND wait_count > 0;
39+
40+
?column? class_id class_name event_id event_name ?column?
41+
42+
t 1 LWLocks 10 CheckpointLock t
43+
step get_history_lwlock:
44+
SELECT
45+
pid > 0,
46+
(now() - sample_ts) < interval '1 hour',
47+
class_id,
48+
class_name,
49+
event_id,
50+
event_name,
51+
wait_time > 0,
52+
p1, p2, p3, p4, p5
53+
FROM pg_stat_wait_history
54+
WHERE class_id=1 AND p5=5;
55+
56+
?column? ?column? class_id class_name event_id event_name ?column? p1 p2 p3 p4 p5
57+
58+
t t 1 LWLocks 10 CheckpointLock t 1 0 0 0 5
59+
60+
starting permutation: start_io get_current_io stop_wait get_profile get_history_io
61+
step start_io:
62+
SELECT pg_stat_wait_start_wait(3, 1, 1, 2, 3, 4, 5);
63+
SELECT pg_sleep(1);
64+
65+
pg_stat_wait_start_wait
66+
67+
68+
pg_sleep
69+
70+
71+
step get_current_io:
72+
SELECT
73+
pid > 0,
74+
(now() - sample_ts) < interval '1 minute',
75+
class_id,
76+
class_name,
77+
event_id,
78+
event_name,
79+
p1, p2, p3, p4, p5
80+
FROM pg_stat_wait_current
81+
WHERE class_id = 3;
82+
83+
?column? ?column? class_id class_name event_id event_name p1 p2 p3 p4 p5
84+
85+
t t 3 Storage 1 SMGR_WRITE 1 2 3 4 5
86+
step stop_wait:
87+
SELECT pg_stat_wait_stop_wait();
88+
89+
pg_stat_wait_stop_wait
90+
91+
92+
step get_profile:
93+
SELECT pid > 0, class_id, class_name, event_id, event_name, wait_time > 0
94+
FROM pg_stat_wait_profile
95+
where class_id = 1 AND event_id = 10 AND wait_count > 0;
96+
97+
?column? class_id class_name event_id event_name ?column?
98+
99+
t 1 LWLocks 10 CheckpointLock t
100+
step get_history_io:
101+
SELECT (count(*) > 0) AS io_wait_recorded
102+
FROM pg_stat_wait_history
103+
WHERE class_id = 3 AND event_id=1 AND p1=1 AND p2=2 AND p3=3 AND p4=4 AND p5=5;
104+
105+
io_wait_recorded
106+
107+
t
+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/* contrib/pg_stat_wait/pg_stat_wait--1.0.sql */
2+
3+
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
4+
\echo Use "CREATE EXTENSION pg_stat_wait" to load this file. \quit
5+
6+
CREATE FUNCTION pg_wait_class_list(
7+
OUT class_id int4,
8+
OUT name cstring
9+
)
10+
RETURNS SETOF record
11+
AS 'MODULE_PATHNAME'
12+
LANGUAGE C VOLATILE;
13+
14+
CREATE FUNCTION pg_wait_event_list(
15+
OUT class_id int4,
16+
OUT event_id int4,
17+
OUT name cstring
18+
)
19+
RETURNS SETOF record
20+
AS 'MODULE_PATHNAME'
21+
LANGUAGE C VOLATILE;
22+
23+
CREATE VIEW pg_wait_event AS
24+
SELECT class_id, event_id, CAST(name as text) as name FROM pg_wait_event_list();
25+
26+
CREATE VIEW pg_wait_class AS
27+
SELECT class_id, CAST(name as text) as name FROM pg_wait_class_list();
28+
29+
CREATE VIEW pg_wait_events AS
30+
SELECT c.class_id, CAST(c.name as text) as class_name, e.event_id, CAST(e.name as text) as event_name
31+
FROM pg_wait_class c
32+
INNER JOIN pg_wait_event e ON c.class_id = e.class_id
33+
ORDER BY c.class_id, e.event_id;
34+
35+
/* Returns history, parameters count must be equal with WAIT_PARAMS_COUNT in proc.h */
36+
CREATE FUNCTION pg_stat_wait_get_history(
37+
OUT pid int4,
38+
OUT sample_ts timestamptz,
39+
OUT class_id int4,
40+
OUT event_id int4,
41+
OUT wait_time int8,
42+
OUT p1 int4,
43+
OUT p2 int4,
44+
OUT p3 int4,
45+
OUT p4 int4,
46+
OUT p5 int4
47+
)
48+
RETURNS SETOF record
49+
AS 'MODULE_PATHNAME'
50+
LANGUAGE C VOLATILE STRICT;
51+
52+
CREATE VIEW pg_stat_wait_history AS
53+
SELECT pid, sample_ts, h.class_id, e.class_name, h.event_id, e.event_name, wait_time, p1, p2, p3, p4, p5
54+
FROM pg_stat_wait_get_history() h
55+
INNER JOIN pg_wait_events e
56+
ON e.class_id = h.class_id and e.event_id = h.event_id;
57+
58+
CREATE FUNCTION pg_stat_wait_get_profile(
59+
pid int4,
60+
reset boolean,
61+
OUT pid int4,
62+
OUT class_id int4,
63+
OUT event_id int4,
64+
OUT wait_time int8,
65+
OUT wait_count int4
66+
)
67+
RETURNS SETOF record
68+
AS 'MODULE_PATHNAME'
69+
LANGUAGE C VOLATILE CALLED ON NULL INPUT;
70+
71+
CREATE VIEW pg_stat_wait_profile AS
72+
SELECT pid, p.class_id, e.class_name, p.event_id, e.event_name, wait_time, wait_count
73+
FROM pg_stat_wait_get_profile(NULL, false) p
74+
INNER JOIN pg_wait_events e
75+
ON e.class_id = p.class_id and e.event_id = p.event_id;
76+
77+
CREATE FUNCTION pg_stat_wait_reset_profile()
78+
RETURNS void
79+
AS 'MODULE_PATHNAME'
80+
LANGUAGE C VOLATILE STRICT;
81+
82+
CREATE FUNCTION pg_stat_wait_get_current(
83+
pid int4,
84+
OUT pid int4,
85+
OUT sample_ts timestamptz,
86+
OUT class_id int4,
87+
OUT event_id int4,
88+
OUT wait_time int8,
89+
OUT p1 int4,
90+
OUT p2 int4,
91+
OUT p3 int4,
92+
OUT p4 int4,
93+
OUT p5 int4
94+
)
95+
RETURNS SETOF record
96+
AS 'MODULE_PATHNAME'
97+
LANGUAGE c VOLATILE CALLED on NULL INPUT;
98+
99+
CREATE VIEW pg_stat_wait_current AS
100+
SELECT pid, sample_ts, c.class_id, e.class_name, c.event_id, e.event_name,
101+
wait_time, p1, p2, p3, p4, p5
102+
FROM pg_stat_wait_get_current(null) c
103+
INNER JOIN pg_wait_events e
104+
ON e.class_id = c.class_id and e.event_id = c.event_id;
105+
106+
CREATE FUNCTION pg_start_trace(
107+
backend_pid int4,
108+
filename cstring
109+
)
110+
RETURNS void
111+
AS 'MODULE_PATHNAME'
112+
LANGUAGE C VOLATILE CALLED ON NULL INPUT;
113+
114+
CREATE FUNCTION pg_is_in_trace(
115+
backend_pid int4
116+
)
117+
RETURNS bool
118+
AS 'MODULE_PATHNAME'
119+
LANGUAGE C VOLATILE CALLED ON NULL INPUT;
120+
121+
CREATE FUNCTION pg_stop_trace(
122+
backend_pid int4
123+
)
124+
RETURNS void
125+
AS 'MODULE_PATHNAME'
126+
LANGUAGE C VOLATILE CALLED ON NULL INPUT;
127+

‎contrib/pg_stat_wait/pg_stat_wait.c

+752
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# pg_stat_wait extension
2+
comment = 'track execution statistics of waits'
3+
default_version = '1.0'
4+
module_pathname = '$libdir/pg_stat_wait'
5+
relocatable = true

‎contrib/pg_stat_wait/pg_stat_wait.h

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#ifndef __PG_STAT_WAIT_H__
2+
#define __PG_STAT_WAIT_H__
3+
4+
#include <postgres.h>
5+
#include "storage/proc.h"
6+
#include "utils/timestamp.h"
7+
8+
#define PG_STAT_WAIT_MAGIC 0xca94b107
9+
#define COLLECTOR_QUEUE_SIZE 16384
10+
#define HISTORY_TIME_MULTIPLIER 10
11+
#define HISTORY_COLUMNS_COUNT (5 + WAIT_PARAMS_COUNT)
12+
13+
typedef struct
14+
{
15+
uint32 backendPid;
16+
bool reset;
17+
int backendIdx;
18+
int classIdx;
19+
int eventIdx;
20+
} WaitProfileContext;
21+
22+
typedef struct
23+
{
24+
int class_cnt;
25+
int event_cnt;
26+
} WaitEventContext;
27+
28+
typedef struct
29+
{
30+
int classId;
31+
int eventId;
32+
int params[WAIT_PARAMS_COUNT];
33+
int backendPid;
34+
uint64 waitTime;
35+
36+
TimestampTz ts;
37+
} HistoryItem;
38+
39+
typedef struct
40+
{
41+
int idx;
42+
HistoryItem *state;
43+
bool done;
44+
TimestampTz ts;
45+
} WaitCurrentContext;
46+
47+
typedef struct
48+
{
49+
bool wraparound;
50+
int index;
51+
int count;
52+
HistoryItem *items;
53+
} History;
54+
55+
typedef enum
56+
{
57+
NO_REQUEST,
58+
HISTORY_REQUEST
59+
} SHMRequest;
60+
61+
typedef struct
62+
{
63+
Latch *latch;
64+
SHMRequest request;
65+
int historySize;
66+
int historyPeriod;
67+
bool historySkipLatch;
68+
} CollectorShmqHeader;
69+
70+
extern PGDLLIMPORT char *WAIT_LOCK_NAMES[];
71+
extern PGDLLIMPORT char *WAIT_LWLOCK_NAMES[];
72+
extern PGDLLIMPORT char *WAIT_IO_NAMES[];
73+
extern PGDLLIMPORT char *WAIT_NETWORK_NAMES[];
74+
extern PGDLLIMPORT const int WAIT_OFFSETS[];
75+
76+
Size CollectorShmemSize(void);
77+
void AllocateCollectorMem(void);
78+
void RegisterWaitsCollector(void);
79+
void AllocHistory(History *, int);
80+
int GetCurrentWaitsState(PGPROC *, HistoryItem *, int);
81+
82+
#endif
+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
setup
2+
{
3+
DROP TABLE IF EXISTS do_write;
4+
CREATE TABLE do_write(id serial primary key);
5+
DROP FUNCTION IF EXISTS pg_stat_wait_start_wait(int4, int4, int4, int4, int4, int4, int4);
6+
DROP FUNCTION IF EXISTS pg_stat_wait_stop_wait();
7+
8+
CREATE FUNCTION pg_stat_wait_start_wait(
9+
class_id int4,
10+
event_id int4,
11+
p1 int4,
12+
p2 int4,
13+
p3 int4,
14+
p4 int4,
15+
p5 int4
16+
)
17+
RETURNS void
18+
AS 'pg_stat_wait.so'
19+
LANGUAGE C VOLATILE STRICT;
20+
21+
CREATE FUNCTION pg_stat_wait_stop_wait()
22+
RETURNS void
23+
AS 'pg_stat_wait.so'
24+
LANGUAGE C VOLATILE STRICT;
25+
}
26+
27+
teardown
28+
{
29+
DROP TABLE IF EXISTS do_write;
30+
}
31+
32+
session "s0"
33+
step "start_lwlock" {
34+
SELECT pg_stat_wait_start_wait(1, 10, 1, 0, 0, 0, 5);
35+
SELECT pg_sleep(1);
36+
}
37+
step "stop_wait" {
38+
SELECT pg_stat_wait_stop_wait();
39+
}
40+
step "start_io" {
41+
SELECT pg_stat_wait_start_wait(3, 1, 1, 2, 3, 4, 5);
42+
SELECT pg_sleep(1);
43+
}
44+
45+
session "s1"
46+
step "get_current_lwlock" {
47+
SELECT
48+
pid > 0,
49+
(now() - sample_ts) < interval '1 hour',
50+
class_id,
51+
class_name,
52+
event_id,
53+
event_name,
54+
p1, p2, p3, p4, p5
55+
FROM pg_stat_wait_current
56+
WHERE class_id = 1;
57+
}
58+
59+
step "get_current_io" {
60+
SELECT
61+
pid > 0,
62+
(now() - sample_ts) < interval '1 minute',
63+
class_id,
64+
class_name,
65+
event_id,
66+
event_name,
67+
p1, p2, p3, p4, p5
68+
FROM pg_stat_wait_current
69+
WHERE class_id = 3;
70+
}
71+
72+
step "get_profile" {
73+
SELECT pid > 0, class_id, class_name, event_id, event_name, wait_time > 0
74+
FROM pg_stat_wait_profile
75+
where class_id = 1 AND event_id = 10 AND wait_count > 0;
76+
}
77+
78+
step "get_history_lwlock" {
79+
SELECT
80+
pid > 0,
81+
(now() - sample_ts) < interval '1 hour',
82+
class_id,
83+
class_name,
84+
event_id,
85+
event_name,
86+
wait_time > 0,
87+
p1, p2, p3, p4, p5
88+
FROM pg_stat_wait_history
89+
WHERE class_id=1 AND p5=5;
90+
}
91+
92+
step "get_history_io" {
93+
SELECT (count(*) > 0) AS io_wait_recorded
94+
FROM pg_stat_wait_history
95+
WHERE class_id = 3 AND event_id=1 AND p1=1 AND p2=2 AND p3=3 AND p4=4 AND p5=5;
96+
}
97+
98+
permutation "start_lwlock" "get_current_lwlock" "stop_wait" "get_profile" "get_history_lwlock"
99+
permutation "start_io" "get_current_io" "stop_wait" "get_profile" "get_history_io"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
SELECT * FROM pg_wait_event;
2+
SELECT * FROM pg_wait_class;
3+
SELECT * FROM pg_wait_events;
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
select pg_start_trace(0, '/tmp/pg_stat_wait.trace');
2+
select pg_is_in_trace(0);
3+
select pg_stop_trace(0);
4+

‎contrib/pg_stat_wait/sql/history.sql

Whitespace-only changes.

‎contrib/pg_stat_wait/waits.conf

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
waits_monitoring = on
2+
waits_flush_period = 10
3+
shared_preload_libraries = 'pg_stat_wait.so'
4+
5+
pg_stat_wait.history = on
6+
pg_stat_wait.history_size = 4096
7+
pg_stat_wait.history_period = 1
8+
pg_stat_wait.history_skip_latch = on

‎src/include/storage/barrier.h

+1-159
Original file line numberDiff line numberDiff line change
@@ -14,166 +14,8 @@
1414
#define BARRIER_H
1515

1616
#include "storage/s_lock.h"
17+
#include "port/atomics.h"
1718

1819
extern slock_t dummy_spinlock;
1920

20-
/*
21-
* A compiler barrier need not (and preferably should not) emit any actual
22-
* machine code, but must act as an optimization fence: the compiler must not
23-
* reorder loads or stores to main memory around the barrier. However, the
24-
* CPU may still reorder loads or stores at runtime, if the architecture's
25-
* memory model permits this.
26-
*
27-
* A memory barrier must act as a compiler barrier, and in addition must
28-
* guarantee that all loads and stores issued prior to the barrier are
29-
* completed before any loads or stores issued after the barrier. Unless
30-
* loads and stores are totally ordered (which is not the case on most
31-
* architectures) this requires issuing some sort of memory fencing
32-
* instruction.
33-
*
34-
* A read barrier must act as a compiler barrier, and in addition must
35-
* guarantee that any loads issued prior to the barrier are completed before
36-
* any loads issued after the barrier. Similarly, a write barrier acts
37-
* as a compiler barrier, and also orders stores. Read and write barriers
38-
* are thus weaker than a full memory barrier, but stronger than a compiler
39-
* barrier. In practice, on machines with strong memory ordering, read and
40-
* write barriers may require nothing more than a compiler barrier.
41-
*
42-
* For an introduction to using memory barriers within the PostgreSQL backend,
43-
* see src/backend/storage/lmgr/README.barrier
44-
*/
45-
46-
#if defined(DISABLE_BARRIERS)
47-
48-
/*
49-
* Fall through to the spinlock-based implementation.
50-
*/
51-
#elif defined(__INTEL_COMPILER)
52-
53-
/*
54-
* icc defines __GNUC__, but doesn't support gcc's inline asm syntax
55-
*/
56-
#if defined(__ia64__) || defined(__ia64)
57-
#define pg_memory_barrier() __mf()
58-
#elif defined(__i386__) || defined(__x86_64__)
59-
#define pg_memory_barrier() _mm_mfence()
60-
#endif
61-
62-
#define pg_compiler_barrier() __memory_barrier()
63-
#elif defined(__GNUC__)
64-
65-
/* This works on any architecture, since it's only talking to GCC itself. */
66-
#define pg_compiler_barrier() __asm__ __volatile__("" : : : "memory")
67-
68-
#if defined(__i386__)
69-
70-
/*
71-
* i386 does not allow loads to be reordered with other loads, or stores to be
72-
* reordered with other stores, but a load can be performed before a subsequent
73-
* store.
74-
*
75-
* "lock; addl" has worked for longer than "mfence".
76-
*/
77-
#define pg_memory_barrier() \
78-
__asm__ __volatile__ ("lock; addl $0,0(%%esp)" : : : "memory", "cc")
79-
#define pg_read_barrier() pg_compiler_barrier()
80-
#define pg_write_barrier() pg_compiler_barrier()
81-
#elif defined(__x86_64__) /* 64 bit x86 */
82-
83-
/*
84-
* x86_64 has similar ordering characteristics to i386.
85-
*
86-
* Technically, some x86-ish chips support uncached memory access and/or
87-
* special instructions that are weakly ordered. In those cases we'd need
88-
* the read and write barriers to be lfence and sfence. But since we don't
89-
* do those things, a compiler barrier should be enough.
90-
*/
91-
#define pg_memory_barrier() \
92-
__asm__ __volatile__ ("lock; addl $0,0(%%rsp)" : : : "memory", "cc")
93-
#define pg_read_barrier() pg_compiler_barrier()
94-
#define pg_write_barrier() pg_compiler_barrier()
95-
#elif defined(__ia64__) || defined(__ia64)
96-
97-
/*
98-
* Itanium is weakly ordered, so read and write barriers require a full
99-
* fence.
100-
*/
101-
#define pg_memory_barrier() __asm__ __volatile__ ("mf" : : : "memory")
102-
#elif defined(__ppc__) || defined(__powerpc__) || defined(__ppc64__) || defined(__powerpc64__)
103-
104-
/*
105-
* lwsync orders loads with respect to each other, and similarly with stores.
106-
* But a load can be performed before a subsequent store, so sync must be used
107-
* for a full memory barrier.
108-
*/
109-
#define pg_memory_barrier() __asm__ __volatile__ ("sync" : : : "memory")
110-
#define pg_read_barrier() __asm__ __volatile__ ("lwsync" : : : "memory")
111-
#define pg_write_barrier() __asm__ __volatile__ ("lwsync" : : : "memory")
112-
#elif defined(__alpha) || defined(__alpha__) /* Alpha */
113-
114-
/*
115-
* Unlike all other known architectures, Alpha allows dependent reads to be
116-
* reordered, but we don't currently find it necessary to provide a conditional
117-
* read barrier to cover that case. We might need to add that later.
118-
*/
119-
#define pg_memory_barrier() __asm__ __volatile__ ("mb" : : : "memory")
120-
#define pg_read_barrier() __asm__ __volatile__ ("mb" : : : "memory")
121-
#define pg_write_barrier() __asm__ __volatile__ ("wmb" : : : "memory")
122-
#elif defined(__hppa) || defined(__hppa__) /* HP PA-RISC */
123-
124-
/* HPPA doesn't do either read or write reordering */
125-
#define pg_memory_barrier() pg_compiler_barrier()
126-
#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)
127-
128-
/*
129-
* If we're on GCC 4.1.0 or higher, we should be able to get a memory
130-
* barrier out of this compiler built-in. But we prefer to rely on our
131-
* own definitions where possible, and use this only as a fallback.
132-
*/
133-
#define pg_memory_barrier() __sync_synchronize()
134-
#endif
135-
#elif defined(__ia64__) || defined(__ia64)
136-
137-
#define pg_compiler_barrier() _Asm_sched_fence()
138-
#define pg_memory_barrier() _Asm_mf()
139-
#elif defined(WIN32_ONLY_COMPILER)
140-
141-
/* Should work on both MSVC and Borland. */
142-
#include <intrin.h>
143-
#pragma intrinsic(_ReadWriteBarrier)
144-
#define pg_compiler_barrier() _ReadWriteBarrier()
145-
#define pg_memory_barrier() MemoryBarrier()
146-
#endif
147-
148-
/*
149-
* If we have no memory barrier implementation for this architecture, we
150-
* fall back to acquiring and releasing a spinlock. This might, in turn,
151-
* fall back to the semaphore-based spinlock implementation, which will be
152-
* amazingly slow.
153-
*
154-
* It's not self-evident that every possible legal implementation of a
155-
* spinlock acquire-and-release would be equivalent to a full memory barrier.
156-
* For example, I'm not sure that Itanium's acq and rel add up to a full
157-
* fence. But all of our actual implementations seem OK in this regard.
158-
*/
159-
#if !defined(pg_memory_barrier)
160-
#define pg_memory_barrier() \
161-
do { S_LOCK(&dummy_spinlock); S_UNLOCK(&dummy_spinlock); } while (0)
162-
#endif
163-
164-
/*
165-
* If read or write barriers are undefined, we upgrade them to full memory
166-
* barriers.
167-
*
168-
* If a compiler barrier is unavailable, you probably don't want a full
169-
* memory barrier instead, so if you have a use case for a compiler barrier,
170-
* you'd better use #ifdef.
171-
*/
172-
#if !defined(pg_read_barrier)
173-
#define pg_read_barrier() pg_memory_barrier()
174-
#endif
175-
#if !defined(pg_write_barrier)
176-
#define pg_write_barrier() pg_memory_barrier()
177-
#endif
178-
17921
#endif /* BARRIER_H */

0 commit comments

Comments
 (0)
Please sign in to comment.