Commit 1e0dfd16 authored by Peter Geoghegan's avatar Peter Geoghegan

Add Valgrind buffer access instrumentation.

Teach Valgrind memcheck to maintain the "defined-ness" of each shared
buffer based on whether the backend holds at least one pin at the point
it is accessed by access method code.  Bugs like the one fixed by commit
b0229f26 can be detected using this new instrumentation.

Note that backends running with Valgrind naturally have their own
independent ideas about whether any given byte in shared memory is safe
or unsafe to access.  There is no risk that concurrent access by
multiple backends to the same shared memory will confuse Valgrind's
instrumentation, because everything already works at the process level
(or at the memory mapping level, if you prefer).

Author: Álvaro Herrera, Peter Geoghegan
Reviewed-By: Anastasia Lubennikova
Discussion: https://postgr.es/m/20150723195349.GW5596@postgresql.org
Discussion: https://postgr.es/m/CAH2-WzkLgyN3zBvRZ1pkNJThC=xi_0gpWRUb_45eexLH1+k2_Q@mail.gmail.com
parent f009591d
...@@ -49,6 +49,7 @@ ...@@ -49,6 +49,7 @@
#include "storage/proc.h" #include "storage/proc.h"
#include "storage/smgr.h" #include "storage/smgr.h"
#include "storage/standby.h" #include "storage/standby.h"
#include "utils/memdebug.h"
#include "utils/ps_status.h" #include "utils/ps_status.h"
#include "utils/rel.h" #include "utils/rel.h"
#include "utils/resowner_private.h" #include "utils/resowner_private.h"
...@@ -1633,6 +1634,13 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) ...@@ -1633,6 +1634,13 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy)
buf_state)) buf_state))
{ {
result = (buf_state & BM_VALID) != 0; result = (buf_state & BM_VALID) != 0;
/*
* If we successfully acquired our first pin on this buffer
* within this backend, mark buffer contents defined
*/
if (result)
VALGRIND_MAKE_MEM_DEFINED(BufHdrGetBlock(buf), BLCKSZ);
break; break;
} }
} }
...@@ -1683,6 +1691,13 @@ PinBuffer_Locked(BufferDesc *buf) ...@@ -1683,6 +1691,13 @@ PinBuffer_Locked(BufferDesc *buf)
*/ */
Assert(GetPrivateRefCountEntry(BufferDescriptorGetBuffer(buf), false) == NULL); Assert(GetPrivateRefCountEntry(BufferDescriptorGetBuffer(buf), false) == NULL);
/*
* Buffer can't have a preexisting pin, so mark its page as defined to
* Valgrind (this is similar to the PinBuffer() case where the backend
* doesn't already have a buffer pin)
*/
VALGRIND_MAKE_MEM_DEFINED(BufHdrGetBlock(buf), BLCKSZ);
/* /*
* Since we hold the buffer spinlock, we can update the buffer state and * Since we hold the buffer spinlock, we can update the buffer state and
* release the lock in one operation. * release the lock in one operation.
...@@ -1728,6 +1743,9 @@ UnpinBuffer(BufferDesc *buf, bool fixOwner) ...@@ -1728,6 +1743,9 @@ UnpinBuffer(BufferDesc *buf, bool fixOwner)
uint32 buf_state; uint32 buf_state;
uint32 old_buf_state; uint32 old_buf_state;
/* Mark undefined, now that no pins remain in backend */
VALGRIND_MAKE_MEM_NOACCESS(BufHdrGetBlock(buf), BLCKSZ);
/* I'd better not still hold any locks on the buffer */ /* I'd better not still hold any locks on the buffer */
Assert(!LWLockHeldByMe(BufferDescriptorGetContentLock(buf))); Assert(!LWLockHeldByMe(BufferDescriptorGetContentLock(buf)));
Assert(!LWLockHeldByMe(BufferDescriptorGetIOLock(buf))); Assert(!LWLockHeldByMe(BufferDescriptorGetIOLock(buf)));
......
...@@ -269,12 +269,13 @@ ...@@ -269,12 +269,13 @@
/* /*
* Include Valgrind "client requests", mostly in the memory allocator, so * Include Valgrind "client requests", mostly in the memory allocator, so
* Valgrind understands PostgreSQL memory contexts. This permits detecting * Valgrind understands PostgreSQL memory contexts. This permits detecting
* memory errors that Valgrind would not detect on a vanilla build. See also * memory errors that Valgrind would not detect on a vanilla build. It also
* src/tools/valgrind.supp. "make installcheck" runs 20-30x longer under * enables detection of buffer accesses that take place without holding a
* Valgrind. Note that USE_VALGRIND slowed older versions of Valgrind by an * buffer pin. See also src/tools/valgrind.supp.
* additional order of magnitude; Valgrind 3.8.1 does not have this problem. *
* The client requests fall in hot code paths, so USE_VALGRIND also slows * "make installcheck" is significantly slower under Valgrind. The client
* native execution by a few percentage points. * requests fall in hot code paths, so USE_VALGRIND slows native execution by
* a few percentage points even when not run under Valgrind.
* *
* You should normally use MEMORY_CONTEXT_CHECKING with USE_VALGRIND; * You should normally use MEMORY_CONTEXT_CHECKING with USE_VALGRIND;
* instrumentation of repalloc() is inferior without it. * instrumentation of repalloc() is inferior without it.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment