Commit 3bdcf6a5 authored by Andres Freund's avatar Andres Freund

Don't allow to disable backend assertions via the debug_assertions GUC.

The existance of the assert_enabled variable (backing the
debug_assertions GUC) reduced the amount of knowledge some static code
checkers (like coverity and various compilers) could infer from the
existance of the assertion. That could have been solved by optionally
removing the assertion_enabled variable from the Assert() et al macros
at compile time when some special macro is defined, but the resulting
complication doesn't seem to be worth the gain from having
debug_assertions. Recompiling is fast enough.

The debug_assertions GUC is still available, but readonly, as it's
useful when diagnosing problems. The commandline/client startup option
-A, which previously also allowed to enable/disable assertions, has
been removed as it doesn't serve a purpose anymore.

While at it, reduce code duplication in bufmgr.c and localbuf.c
assertions checking for spurious buffer pins. That code had to be
reindented anyway to cope with the assert_enabled removal.
parent 45b0f357
...@@ -6722,6 +6722,26 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir' ...@@ -6722,6 +6722,26 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry id="guc-debug-assertions" xreflabel="debug_assertions">
<term><varname>debug_assertions</varname> (<type>boolean</type>)
<indexterm>
<primary><varname>debug_assertions</> configuration parameter</primary>
</indexterm>
</term>
<listitem>
<para>
Reports whether <productname>PostgreSQL</productname> has been built
with assertions enabled. That is the case if the
macro <symbol>USE_ASSERT_CHECKING</symbol> is defined
when <productname>PostgreSQL</productname> is built (accomplished
e.g. by the <command>configure</command> option
<option>--enable-cassert</option>). By
default <productname>PostgreSQL</productname> is built without
assertions.
</para>
</listitem>
</varlistentry>
<varlistentry id="guc-integer-datetimes" xreflabel="integer_datetimes"> <varlistentry id="guc-integer-datetimes" xreflabel="integer_datetimes">
<term><varname>integer_datetimes</varname> (<type>boolean</type>) <term><varname>integer_datetimes</varname> (<type>boolean</type>)
<indexterm> <indexterm>
...@@ -6973,28 +6993,6 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir' ...@@ -6973,28 +6993,6 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir'
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry id="guc-debug-assertions" xreflabel="debug_assertions">
<term><varname>debug_assertions</varname> (<type>boolean</type>)
<indexterm>
<primary><varname>debug_assertions</> configuration parameter</primary>
</indexterm>
</term>
<listitem>
<para>
Turns on various assertion checks. This is a debugging aid. If
you are experiencing strange problems or crashes you might want
to turn this on, as it might expose programming mistakes. To use
this parameter, the macro <symbol>USE_ASSERT_CHECKING</symbol>
must be defined when <productname>PostgreSQL</productname> is
built (accomplished by the <command>configure</command> option
<option>--enable-cassert</option>). Note that
<varname>debug_assertions</varname> defaults to <literal>on</>
if <productname>PostgreSQL</productname> has been built with
assertions enabled.
</para>
</listitem>
</varlistentry>
<varlistentry id="guc-ignore-system-indexes" xreflabel="ignore_system_indexes"> <varlistentry id="guc-ignore-system-indexes" xreflabel="ignore_system_indexes">
<term><varname>ignore_system_indexes</varname> (<type>boolean</type>) <term><varname>ignore_system_indexes</varname> (<type>boolean</type>)
<indexterm> <indexterm>
...@@ -7354,10 +7352,6 @@ LOG: CleanUpLock: deleting: lock(0xb7acd844) id(24688,24696,0,0,0,1) ...@@ -7354,10 +7352,6 @@ LOG: CleanUpLock: deleting: lock(0xb7acd844) id(24688,24696,0,0,0,1)
</thead> </thead>
<tbody> <tbody>
<row>
<entry><option>-A <replaceable>x</replaceable></option></entry>
<entry><literal>debug_assertions = <replaceable>x</replaceable></></entry>
</row>
<row> <row>
<entry><option>-B <replaceable>x</replaceable></option></entry> <entry><option>-B <replaceable>x</replaceable></option></entry>
<entry><literal>shared_buffers = <replaceable>x</replaceable></></entry> <entry><literal>shared_buffers = <replaceable>x</replaceable></></entry>
......
...@@ -101,18 +101,6 @@ PostgreSQL documentation ...@@ -101,18 +101,6 @@ PostgreSQL documentation
<title>General Purpose</title> <title>General Purpose</title>
<variablelist> <variablelist>
<varlistentry>
<term><option>-A 0|1</option></term>
<listitem>
<para>
Enables run-time assertion checks, which is a debugging aid to
detect programming mistakes. This option is only available if
assertions were enabled when <productname>PostgreSQL</> was
compiled. If so, the default is on.
</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>-B <replaceable class="parameter">nbuffers</replaceable></option></term> <term><option>-B <replaceable class="parameter">nbuffers</replaceable></option></term>
<listitem> <listitem>
......
...@@ -250,7 +250,6 @@ ginCompressPostingList(const ItemPointer ipd, int nipd, int maxsize, ...@@ -250,7 +250,6 @@ ginCompressPostingList(const ItemPointer ipd, int nipd, int maxsize,
* Check that the encoded segment decodes back to the original items. * Check that the encoded segment decodes back to the original items.
*/ */
#if defined (CHECK_ENCODING_ROUNDTRIP) #if defined (CHECK_ENCODING_ROUNDTRIP)
if (assert_enabled)
{ {
int ndecoded; int ndecoded;
ItemPointer tmp = ginPostingListDecode(result, &ndecoded); ItemPointer tmp = ginPostingListDecode(result, &ndecoded);
......
...@@ -635,7 +635,6 @@ EventTriggerCommonSetup(Node *parsetree, ...@@ -635,7 +635,6 @@ EventTriggerCommonSetup(Node *parsetree,
* relevant command tag. * relevant command tag.
*/ */
#ifdef USE_ASSERT_CHECKING #ifdef USE_ASSERT_CHECKING
if (assert_enabled)
{ {
const char *dbgtag; const char *dbgtag;
......
...@@ -603,14 +603,10 @@ PostmasterMain(int argc, char *argv[]) ...@@ -603,14 +603,10 @@ PostmasterMain(int argc, char *argv[])
* tcop/postgres.c (the option sets should not conflict) and with the * tcop/postgres.c (the option sets should not conflict) and with the
* common help() function in main/main.c. * common help() function in main/main.c.
*/ */
while ((opt = getopt(argc, argv, "A:B:bc:C:D:d:EeFf:h:ijk:lN:nOo:Pp:r:S:sTt:W:-:")) != -1) while ((opt = getopt(argc, argv, "B:bc:C:D:d:EeFf:h:ijk:lN:nOo:Pp:r:S:sTt:W:-:")) != -1)
{ {
switch (opt) switch (opt)
{ {
case 'A':
SetConfigOption("debug_assertions", optarg, PGC_POSTMASTER, PGC_S_ARGV);
break;
case 'B': case 'B':
SetConfigOption("shared_buffers", optarg, PGC_POSTMASTER, PGC_S_ARGV); SetConfigOption("shared_buffers", optarg, PGC_POSTMASTER, PGC_S_ARGV);
break; break;
......
...@@ -109,6 +109,7 @@ static volatile BufferDesc *BufferAlloc(SMgrRelation smgr, ...@@ -109,6 +109,7 @@ static volatile BufferDesc *BufferAlloc(SMgrRelation smgr,
bool *foundPtr); bool *foundPtr);
static void FlushBuffer(volatile BufferDesc *buf, SMgrRelation reln); static void FlushBuffer(volatile BufferDesc *buf, SMgrRelation reln);
static void AtProcExit_Buffers(int code, Datum arg); static void AtProcExit_Buffers(int code, Datum arg);
static void CheckForBufferLeaks(void);
static int rnode_comparator(const void *p1, const void *p2); static int rnode_comparator(const void *p1, const void *p2);
...@@ -1699,34 +1700,13 @@ SyncOneBuffer(int buf_id, bool skip_recently_used) ...@@ -1699,34 +1700,13 @@ SyncOneBuffer(int buf_id, bool skip_recently_used)
return result | BUF_WRITTEN; return result | BUF_WRITTEN;
} }
/* /*
* AtEOXact_Buffers - clean up at end of transaction. * AtEOXact_Buffers - clean up at end of transaction.
*
* As of PostgreSQL 8.0, buffer pins should get released by the
* ResourceOwner mechanism. This routine is just a debugging
* cross-check that no pins remain.
*/ */
void void
AtEOXact_Buffers(bool isCommit) AtEOXact_Buffers(bool isCommit)
{ {
#ifdef USE_ASSERT_CHECKING CheckForBufferLeaks();
if (assert_enabled)
{
int RefCountErrors = 0;
Buffer b;
for (b = 1; b <= NBuffers; b++)
{
if (PrivateRefCount[b - 1] != 0)
{
PrintBufferLeakWarning(b);
RefCountErrors++;
}
}
Assert(RefCountErrors == 0);
}
#endif
AtEOXact_LocalBuffers(isCommit); AtEOXact_LocalBuffers(isCommit);
} }
...@@ -1756,26 +1736,36 @@ AtProcExit_Buffers(int code, Datum arg) ...@@ -1756,26 +1736,36 @@ AtProcExit_Buffers(int code, Datum arg)
AbortBufferIO(); AbortBufferIO();
UnlockBuffers(); UnlockBuffers();
CheckForBufferLeaks();
/* localbuf.c needs a chance too */
AtProcExit_LocalBuffers();
}
/*
* CheckForBufferLeaks - ensure this backend holds no buffer pins
*
* As of PostgreSQL 8.0, buffer pins should get released by the
* ResourceOwner mechanism. This routine is just a debugging
* cross-check that no pins remain.
*/
static void
CheckForBufferLeaks(void)
{
#ifdef USE_ASSERT_CHECKING #ifdef USE_ASSERT_CHECKING
if (assert_enabled) int RefCountErrors = 0;
{ Buffer b;
int RefCountErrors = 0;
Buffer b;
for (b = 1; b <= NBuffers; b++) for (b = 1; b <= NBuffers; b++)
{
if (PrivateRefCount[b - 1] != 0)
{ {
if (PrivateRefCount[b - 1] != 0) PrintBufferLeakWarning(b);
{ RefCountErrors++;
PrintBufferLeakWarning(b);
RefCountErrors++;
}
} }
Assert(RefCountErrors == 0);
} }
Assert(RefCountErrors == 0);
#endif #endif
/* localbuf.c needs a chance too */
AtProcExit_LocalBuffers();
} }
/* /*
......
...@@ -491,15 +491,15 @@ GetLocalBufferStorage(void) ...@@ -491,15 +491,15 @@ GetLocalBufferStorage(void)
} }
/* /*
* AtEOXact_LocalBuffers - clean up at end of transaction. * CheckForLocalBufferLeaks - ensure this backend holds no local buffer pins
* *
* This is just like AtEOXact_Buffers, but for local buffers. * This is just like CheckBufferLeaks(), but for local buffers.
*/ */
void static void
AtEOXact_LocalBuffers(bool isCommit) CheckForLocalBufferLeaks(void)
{ {
#ifdef USE_ASSERT_CHECKING #ifdef USE_ASSERT_CHECKING
if (assert_enabled && LocalRefCount) if (LocalRefCount)
{ {
int RefCountErrors = 0; int RefCountErrors = 0;
int i; int i;
...@@ -519,34 +519,29 @@ AtEOXact_LocalBuffers(bool isCommit) ...@@ -519,34 +519,29 @@ AtEOXact_LocalBuffers(bool isCommit)
#endif #endif
} }
/*
* AtEOXact_LocalBuffers - clean up at end of transaction.
*
* This is just like AtEOXact_Buffers, but for local buffers.
*/
void
AtEOXact_LocalBuffers(bool isCommit)
{
CheckForLocalBufferLeaks();
}
/* /*
* AtProcExit_LocalBuffers - ensure we have dropped pins during backend exit. * AtProcExit_LocalBuffers - ensure we have dropped pins during backend exit.
* *
* This is just like AtProcExit_Buffers, but for local buffers. We shouldn't * This is just like AtProcExit_Buffers, but for local buffers.
* be holding any remaining pins; if we are, and assertions aren't enabled,
* we'll fail later in DropRelFileNodeBuffers while trying to drop the temp
* rels.
*/ */
void void
AtProcExit_LocalBuffers(void) AtProcExit_LocalBuffers(void)
{ {
#ifdef USE_ASSERT_CHECKING /*
if (assert_enabled && LocalRefCount) * We shouldn't be holding any remaining pins; if we are, and assertions
{ * aren't enabled, we'll fail later in DropRelFileNodeBuffers while trying
int RefCountErrors = 0; * to drop the temp rels.
int i; */
CheckForLocalBufferLeaks();
for (i = 0; i < NLocBuffer; i++)
{
if (LocalRefCount[i] != 0)
{
Buffer b = -i - 1;
PrintBufferLeakWarning(b);
RefCountErrors++;
}
}
Assert(RefCountErrors == 0);
}
#endif
} }
...@@ -376,7 +376,6 @@ InitProcess(void) ...@@ -376,7 +376,6 @@ InitProcess(void)
MyProc->waitLock = NULL; MyProc->waitLock = NULL;
MyProc->waitProcLock = NULL; MyProc->waitProcLock = NULL;
#ifdef USE_ASSERT_CHECKING #ifdef USE_ASSERT_CHECKING
if (assert_enabled)
{ {
int i; int i;
...@@ -539,7 +538,6 @@ InitAuxiliaryProcess(void) ...@@ -539,7 +538,6 @@ InitAuxiliaryProcess(void)
MyProc->waitLock = NULL; MyProc->waitLock = NULL;
MyProc->waitProcLock = NULL; MyProc->waitProcLock = NULL;
#ifdef USE_ASSERT_CHECKING #ifdef USE_ASSERT_CHECKING
if (assert_enabled)
{ {
int i; int i;
...@@ -782,7 +780,6 @@ ProcKill(int code, Datum arg) ...@@ -782,7 +780,6 @@ ProcKill(int code, Datum arg)
SyncRepCleanupAtProcExit(); SyncRepCleanupAtProcExit();
#ifdef USE_ASSERT_CHECKING #ifdef USE_ASSERT_CHECKING
if (assert_enabled)
{ {
int i; int i;
......
...@@ -3305,14 +3305,10 @@ process_postgres_switches(int argc, char *argv[], GucContext ctx, ...@@ -3305,14 +3305,10 @@ process_postgres_switches(int argc, char *argv[], GucContext ctx,
* postmaster/postmaster.c (the option sets should not conflict) and with * postmaster/postmaster.c (the option sets should not conflict) and with
* the common help() function in main/main.c. * the common help() function in main/main.c.
*/ */
while ((flag = getopt(argc, argv, "A:B:bc:C:D:d:EeFf:h:ijk:lN:nOo:Pp:r:S:sTt:v:W:-:")) != -1) while ((flag = getopt(argc, argv, "B:bc:C:D:d:EeFf:h:ijk:lN:nOo:Pp:r:S:sTt:v:W:-:")) != -1)
{ {
switch (flag) switch (flag)
{ {
case 'A':
SetConfigOption("debug_assertions", optarg, ctx, gucsource);
break;
case 'B': case 'B':
SetConfigOption("shared_buffers", optarg, ctx, gucsource); SetConfigOption("shared_buffers", optarg, ctx, gucsource);
break; break;
......
...@@ -553,41 +553,38 @@ void ...@@ -553,41 +553,38 @@ void
AtEOXact_CatCache(bool isCommit) AtEOXact_CatCache(bool isCommit)
{ {
#ifdef USE_ASSERT_CHECKING #ifdef USE_ASSERT_CHECKING
if (assert_enabled) slist_iter cache_iter;
slist_foreach(cache_iter, &CacheHdr->ch_caches)
{ {
slist_iter cache_iter; CatCache *ccp = slist_container(CatCache, cc_next, cache_iter.cur);
dlist_iter iter;
int i;
slist_foreach(cache_iter, &CacheHdr->ch_caches) /* Check CatCLists */
dlist_foreach(iter, &ccp->cc_lists)
{ {
CatCache *ccp = slist_container(CatCache, cc_next, cache_iter.cur); CatCList *cl;
dlist_iter iter;
int i;
/* Check CatCLists */ cl = dlist_container(CatCList, cache_elem, iter.cur);
dlist_foreach(iter, &ccp->cc_lists) Assert(cl->cl_magic == CL_MAGIC);
{ Assert(cl->refcount == 0);
CatCList *cl; Assert(!cl->dead);
}
cl = dlist_container(CatCList, cache_elem, iter.cur); /* Check individual tuples */
Assert(cl->cl_magic == CL_MAGIC); for (i = 0; i < ccp->cc_nbuckets; i++)
Assert(cl->refcount == 0); {
Assert(!cl->dead); dlist_head *bucket = &ccp->cc_bucket[i];
}
/* Check individual tuples */ dlist_foreach(iter, bucket)
for (i = 0; i < ccp->cc_nbuckets; i++)
{ {
dlist_head *bucket = &ccp->cc_bucket[i]; CatCTup *ct;
dlist_foreach(iter, bucket)
{
CatCTup *ct;
ct = dlist_container(CatCTup, cache_elem, iter.cur); ct = dlist_container(CatCTup, cache_elem, iter.cur);
Assert(ct->ct_magic == CT_MAGIC); Assert(ct->ct_magic == CT_MAGIC);
Assert(ct->refcount == 0); Assert(ct->refcount == 0);
Assert(!ct->dead); Assert(!ct->dead);
}
} }
} }
} }
......
...@@ -220,7 +220,6 @@ RelidByRelfilenode(Oid reltablespace, Oid relfilenode) ...@@ -220,7 +220,6 @@ RelidByRelfilenode(Oid reltablespace, Oid relfilenode)
found = true; found = true;
#ifdef USE_ASSERT_CHECKING #ifdef USE_ASSERT_CHECKING
if (assert_enabled)
{ {
bool isnull; bool isnull;
Oid check; Oid check;
......
...@@ -174,7 +174,6 @@ static void assign_syslog_ident(const char *newval, void *extra); ...@@ -174,7 +174,6 @@ static void assign_syslog_ident(const char *newval, void *extra);
static void assign_session_replication_role(int newval, void *extra); static void assign_session_replication_role(int newval, void *extra);
static bool check_temp_buffers(int *newval, void **extra, GucSource source); static bool check_temp_buffers(int *newval, void **extra, GucSource source);
static bool check_phony_autocommit(bool *newval, void **extra, GucSource source); static bool check_phony_autocommit(bool *newval, void **extra, GucSource source);
static bool check_debug_assertions(bool *newval, void **extra, GucSource source);
static bool check_bonjour(bool *newval, void **extra, GucSource source); static bool check_bonjour(bool *newval, void **extra, GucSource source);
static bool check_ssl(bool *newval, void **extra, GucSource source); static bool check_ssl(bool *newval, void **extra, GucSource source);
static bool check_stage_log_stats(bool *newval, void **extra, GucSource source); static bool check_stage_log_stats(bool *newval, void **extra, GucSource source);
...@@ -413,11 +412,6 @@ extern const struct config_enum_entry dynamic_shared_memory_options[]; ...@@ -413,11 +412,6 @@ extern const struct config_enum_entry dynamic_shared_memory_options[];
/* /*
* GUC option variables that are exported from this module * GUC option variables that are exported from this module
*/ */
#ifdef USE_ASSERT_CHECKING
bool assert_enabled = true;
#else
bool assert_enabled = false;
#endif
bool log_duration = false; bool log_duration = false;
bool Debug_print_plan = false; bool Debug_print_plan = false;
bool Debug_print_parse = false; bool Debug_print_parse = false;
...@@ -500,6 +494,7 @@ static bool data_checksums; ...@@ -500,6 +494,7 @@ static bool data_checksums;
static int wal_segment_size; static int wal_segment_size;
static bool integer_datetimes; static bool integer_datetimes;
static int effective_io_concurrency; static int effective_io_concurrency;
static bool assert_enabled;
/* should be static, but commands/variable.c needs to get at this */ /* should be static, but commands/variable.c needs to get at this */
char *role_string; char *role_string;
...@@ -931,10 +926,10 @@ static struct config_bool ConfigureNamesBool[] = ...@@ -931,10 +926,10 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL NULL, NULL, NULL
}, },
{ {
{"debug_assertions", PGC_USERSET, DEVELOPER_OPTIONS, {"debug_assertions", PGC_INTERNAL, PRESET_OPTIONS,
gettext_noop("Turns on various assertion checks."), gettext_noop("Shows whether the running server has assertion checks enabled."),
gettext_noop("This is a debugging aid."), NULL,
GUC_NOT_IN_SAMPLE GUC_NOT_IN_SAMPLE | GUC_DISALLOW_IN_FILE
}, },
&assert_enabled, &assert_enabled,
#ifdef USE_ASSERT_CHECKING #ifdef USE_ASSERT_CHECKING
...@@ -942,7 +937,7 @@ static struct config_bool ConfigureNamesBool[] = ...@@ -942,7 +937,7 @@ static struct config_bool ConfigureNamesBool[] =
#else #else
false, false,
#endif #endif
check_debug_assertions, NULL, NULL NULL, NULL, NULL
}, },
{ {
...@@ -9117,19 +9112,6 @@ check_phony_autocommit(bool *newval, void **extra, GucSource source) ...@@ -9117,19 +9112,6 @@ check_phony_autocommit(bool *newval, void **extra, GucSource source)
return true; return true;
} }
static bool
check_debug_assertions(bool *newval, void **extra, GucSource source)
{
#ifndef USE_ASSERT_CHECKING
if (*newval)
{
GUC_check_errmsg("assertion checking is not supported by this build");
return false;
}
#endif
return true;
}
static bool static bool
check_bonjour(bool *newval, void **extra, GucSource source) check_bonjour(bool *newval, void **extra, GucSource source)
{ {
......
...@@ -597,7 +597,7 @@ typedef NameData *Name; ...@@ -597,7 +597,7 @@ typedef NameData *Name;
*/ */
#define Trap(condition, errorType) \ #define Trap(condition, errorType) \
do { \ do { \
if ((assert_enabled) && (condition)) \ if (condition) \
ExceptionalCondition(CppAsString(condition), (errorType), \ ExceptionalCondition(CppAsString(condition), (errorType), \
__FILE__, __LINE__); \ __FILE__, __LINE__); \
} while (0) } while (0)
...@@ -610,7 +610,7 @@ typedef NameData *Name; ...@@ -610,7 +610,7 @@ typedef NameData *Name;
* Isn't CPP fun? * Isn't CPP fun?
*/ */
#define TrapMacro(condition, errorType) \ #define TrapMacro(condition, errorType) \
((bool) ((! assert_enabled) || ! (condition) || \ ((bool) (! (condition) || \
(ExceptionalCondition(CppAsString(condition), (errorType), \ (ExceptionalCondition(CppAsString(condition), (errorType), \
__FILE__, __LINE__), 0))) __FILE__, __LINE__), 0)))
......
...@@ -680,13 +680,10 @@ extern Datum Float8GetDatum(float8 X); ...@@ -680,13 +680,10 @@ extern Datum Float8GetDatum(float8 X);
*/ */
/* /*
* These declarations supports the assertion-related macros in c.h. * Backend only infrastructure for the the assertion-related macros in c.h.
* assert_enabled is here because that file doesn't have PGDLLIMPORT in the *
* right place, and ExceptionalCondition must be present, for the backend only, * ExceptionalCondition must be present even when assertions are not enabled.
* even when assertions are not enabled.
*/ */
extern PGDLLIMPORT bool assert_enabled;
extern void ExceptionalCondition(const char *conditionName, extern void ExceptionalCondition(const char *conditionName,
const char *errorType, const char *errorType,
const char *fileName, int lineNumber) __attribute__((noreturn)); const char *fileName, int lineNumber) __attribute__((noreturn));
......
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