Thread
Commits
-
Improve our support for Valgrind's leak tracking.
- bb049a79d344 19 (unreleased) landed
-
Increment xactCompletionCount during subtransaction abort.
- 90c885cdab8b 14.0 landed
-
Adjust design of per-worker parallel seqscan data struct
- af527705edc3 14.0 landed
-
Don't leak compiled regex(es) when an ispell cache entry is dropped.
- d303849b059c 14.0 landed
- eba939551afe 13.3 landed
- 92bc14a1027d 12.7 landed
- 0b618ddf8bb2 10.17 landed
- 09e961929614 9.6.22 landed
- 099d2914f30b 11.12 landed
-
Don't leak malloc'd error string in libpqrcv_check_conninfo().
- 9bacdf9f536a 14.0 landed
- ba986b7bc5cf 10.17 landed
- 4eca51d44641 12.7 landed
- 20f11ca0dbc2 11.12 landed
- 12354839e874 13.3 landed
-
Don't leak malloc'd strings when a GUC setting is rejected.
- 377b7a83007d 14.0 landed
- fc552f8680a7 12.7 landed
- 7e25217701cc 9.6.22 landed
- 642b0b69b063 13.3 landed
- 5058e95a6ef9 10.17 landed
- 26a3ae06d85c 11.12 landed
-
Don't leak rd_statlist when a relcache entry is dropped.
- 28644fac1073 14.0 landed
- fbcc9fe33c43 12.7 landed
- 967b693eaef1 11.12 landed
- 536836970144 13.3 landed
- 2bed650c4841 10.17 landed
-
Don't run RelationInitTableAccessMethod in a long-lived context.
- 415ffdc2205e 14.0 landed
- ea3989f3496c 13.3 landed
- 1452a0bb87c7 12.7 landed
-
Getting better results from valgrind leak tracking
Tom Lane <tgl@sss.pgh.pa.us> — 2021-03-16T23:36:10Z
[ starting a new thread for this ] Andres Freund <andres@anarazel.de> writes: > I wonder if it'd be worth starting to explicitly annotate all the places > that do allocations and are fine with leaking them. E.g. by introducing > malloc_permanently() or such. Right now it's hard to use valgrind et al > to detect leaks because of all the false positives due to such "ok to > leak" allocations. Out of curiosity I poked at this for a little while. It doesn't appear to me that we leak much at all, at least not if you are willing to take "still reachable" blocks as not-leaked. Most of the problem is more subtle than that. I found just a couple of things that really seem like leaks of permanent data structures to valgrind: * Where ps_status.c copies the original "environ" array (on PS_USE_CLOBBER_ARGV platforms), valgrind thinks that's all leaked, implying that it doesn't count the "environ" global as a valid reference to leakable data. I was able to shut that up by also saving the pointer into an otherwise-unused static variable. (This is sort of a poor man's implementation of your "malloc_permanently" idea; but I doubt it's worth working harder, given the small number of use-cases.) * The postmaster's sock_paths and lock_files lists appear to be leaked, but we're doing that to ourselves by throwing away the pointers to them without physically freeing the lists. We can just not do that. What I found out is that we have a lot of issues that seem to devolve to valgrind not being sure that a block is referenced. I identified two main causes of that: (1) We have a pointer, but it doesn't actually point right at the start of the block. A primary culprit here is lists of thingies that use the slist and dlist infrastructure. As an experiment, I moved the dlist_node fields of some popular structs to the beginning, and verified that that silences associated complaints. I'm not sure that we want to insist on put-the-link-first as policy (although if we did, it could provide some notational savings perhaps). However, unless someone knows of a way to teach valgrind about this situation, there may be no other way to silence those leakage complaints. A secondary culprit is people randomly applying CACHELINEALIGN or the like to a palloc'd address, so that the address we have isn't pointing right at the block start. (2) The only pointer to the start of a block is actually somewhere within the block. This is common in dynahash tables, where we allocate a slab of entries in a single palloc and then thread them together. Each entry should have exactly one referencing pointer, but that pointer is more likely to be elsewhere within the same palloc block than in the external hash bucket array. AFAICT, all cases except where the slab's first entry is pointed to by a hash bucket pointer confuse valgrind to some extent. I was able to hack around this by preventing dynahash from allocating more than one hash entry per palloc, but I wonder if there's a better way. Attached is a very crude hack, not meant for commit, that hacks things up enough to greatly reduce the number of complaints with "--leak-check=full". One thing I've failed to silence so far is a bunch of entries like ==00:00:03:56.088 3467702== 1,861 bytes in 67 blocks are definitely lost in loss record 1,290 of 1,418 ==00:00:03:56.088 3467702== at 0x950650: MemoryContextAlloc (mcxt.c:827) ==00:00:03:56.088 3467702== by 0x951710: MemoryContextStrdup (mcxt.c:1179) ==00:00:03:56.088 3467702== by 0x91C86E: RelationInitIndexAccessInfo (relcache.c:1444) ==00:00:03:56.088 3467702== by 0x91DA9C: RelationBuildDesc (relcache.c:1200) which is complaining about the memory context identifiers for system indexes' rd_indexcxt contexts. Those are surely not being leaked in any real sense. I suspect that this has something to do with valgrind not counting the context->ident fields as live pointers, but I don't have enough valgrind-fu to fix that. Anyway, the bottom line is that I do not think that we have all that many uses of the pattern you postulated originally. It's more that we've designed some valgrind-unfriendly data structures. We need to improve that situation to make much progress here. regards, tom lane
-
Re: Getting better results from valgrind leak tracking
Andres Freund <andres@anarazel.de> — 2021-03-17T02:31:01Z
Hi, David, there's a question about a commit of yours below, hence adding you. On 2021-03-16 19:36:10 -0400, Tom Lane wrote: > Out of curiosity I poked at this for a little while. Cool. > It doesn't appear to me that we leak much at all, at least not if you > are willing to take "still reachable" blocks as not-leaked. Well, I think for any sort of automated testing - which I think would be useful - we'd really need *no* leaks. I know that I get a few bleats whenever I forget to set --leak-check=no. It's also not just postgres itself, but some of the helper tools... And it's not just valgrind, also gcc/clang sanitizers... > What I found out is that we have a lot of issues that seem to devolve > to valgrind not being sure that a block is referenced. I identified > two main causes of that: > > (1) We have a pointer, but it doesn't actually point right at the start > of the block. A primary culprit here is lists of thingies that use the > slist and dlist infrastructure. As an experiment, I moved the dlist_node > fields of some popular structs to the beginning, and verified that that > silences associated complaints. I'm not sure that we want to insist on > put-the-link-first as policy (although if we did, it could provide some > notational savings perhaps). However, unless someone knows of a way to > teach valgrind about this situation, there may be no other way to silence > those leakage complaints. A secondary culprit is people randomly applying > CACHELINEALIGN or the like to a palloc'd address, so that the address we > have isn't pointing right at the block start. Hm, do you still have a backtrace / suppression for one of those? I didn't see any in a quick (*) serial installcheck I just ran. Or I wasn't able to pinpoint them to this issue. I think the run might have shown a genuine leak: ==2048803== 16 bytes in 1 blocks are definitely lost in loss record 139 of 906 ==2048803== at 0x89D2EA: palloc (mcxt.c:975) ==2048803== by 0x2392D3: heap_beginscan (heapam.c:1198) ==2048803== by 0x264E8F: table_beginscan_strat (tableam.h:918) ==2048803== by 0x265994: systable_beginscan (genam.c:453) ==2048803== by 0x83C2D1: SearchCatCacheMiss (catcache.c:1359) ==2048803== by 0x83C197: SearchCatCacheInternal (catcache.c:1299) ==2048803== by 0x83BE9A: SearchCatCache1 (catcache.c:1167) ==2048803== by 0x85876A: SearchSysCache1 (syscache.c:1134) ==2048803== by 0x84CDB3: RelationInitTableAccessMethod (relcache.c:1795) ==2048803== by 0x84F807: RelationBuildLocalRelation (relcache.c:3554) ==2048803== by 0x303C9D: heap_create (heap.c:395) ==2048803== by 0x305790: heap_create_with_catalog (heap.c:1291) ==2048803== by 0x41A327: DefineRelation (tablecmds.c:885) ==2048803== by 0x6C96B6: ProcessUtilitySlow (utility.c:1131) ==2048803== by 0x6C948A: standard_ProcessUtility (utility.c:1034) ==2048803== by 0x6C865F: ProcessUtility (utility.c:525) ==2048803== by 0x6C7409: PortalRunUtility (pquery.c:1159) ==2048803== by 0x6C7636: PortalRunMulti (pquery.c:1305) ==2048803== by 0x6C6B11: PortalRun (pquery.c:779) ==2048803== by 0x6C05AB: exec_simple_query (postgres.c:1173) ==2048803== { <insert_a_suppression_name_here> Memcheck:Leak match-leak-kinds: definite fun:palloc fun:heap_beginscan fun:table_beginscan_strat fun:systable_beginscan fun:SearchCatCacheMiss fun:SearchCatCacheInternal fun:SearchCatCache1 fun:SearchSysCache1 fun:RelationInitTableAccessMethod fun:RelationBuildLocalRelation fun:heap_create fun:heap_create_with_catalog fun:DefineRelation fun:ProcessUtilitySlow fun:standard_ProcessUtility fun:ProcessUtility fun:PortalRunUtility fun:PortalRunMulti fun:PortalRun fun:exec_simple_query } Since 56788d2156fc heap_beginscan() allocates scan->rs_base.rs_private = palloc(sizeof(ParallelBlockTableScanWorkerData)); in heap_beginscan(). But doesn't free it in heap_endscan(). In most of the places heap scans are begun inside transient contexts, but not always. In the above trace for example RelationBuildLocalRelation switched to CacheMemoryContext, and nothing switched to something else. I'm a bit confused about the precise design of rs_private / ParallelBlockTableScanWorkerData, specifically why it's been added to TableScanDesc, instead of just adding it to HeapScanDesc? And why is it allocated unconditionally, instead of just for parallel scans? I don't think this is a false positive, even though it theoretically could be freed by resetting CacheMemoryContext (see below)? I saw a lot of false positives from autovacuum workers, because AutovacMemCxt is never deleted, and because table_toast_map is created in TopMemoryContext. Adding an explicit MemoryContextDelete(AutovacMemCxt) and parenting table_toast_map in that shut that up. Which brings me to the question why allocations in CacheMemoryContext, AutovacMemCxt are considered to be "lost", even though they're still "reachable" via a context reset. I think valgrind ends up treating memory allocated via memory contexts that still exist at process end as lost, regardless of being reachable via the the memory pool (from valgrinds view). Which I guess actually makes sense, for things like TopMemoryContext and CacheContext - anything not reachable by means other than a context reset is effectively lost for those. For autovac launcher / worker it seems like a sensible thing to just delete AutovacMemCxt. > (2) The only pointer to the start of a block is actually somewhere within > the block. This is common in dynahash tables, where we allocate a slab > of entries in a single palloc and then thread them together. Each entry > should have exactly one referencing pointer, but that pointer is more > likely to be elsewhere within the same palloc block than in the external > hash bucket array. AFAICT, all cases except where the slab's first entry > is pointed to by a hash bucket pointer confuse valgrind to some extent. > I was able to hack around this by preventing dynahash from allocating > more than one hash entry per palloc, but I wonder if there's a better way. Hm. For me the number of leaks seem to stay the same with or without your changes related to this. Is this a USE_VALGRIND build? I'm using valgrind-3.16.1 > Attached is a very crude hack, not meant for commit, that hacks things > up enough to greatly reduce the number of complaints with > "--leak-check=full". > > One thing I've failed to silence so far is a bunch of entries like > > ==00:00:03:56.088 3467702== 1,861 bytes in 67 blocks are definitely lost in loss record 1,290 of 1,418 > ==00:00:03:56.088 3467702== at 0x950650: MemoryContextAlloc (mcxt.c:827) > ==00:00:03:56.088 3467702== by 0x951710: MemoryContextStrdup (mcxt.c:1179) > ==00:00:03:56.088 3467702== by 0x91C86E: RelationInitIndexAccessInfo (relcache.c:1444) > ==00:00:03:56.088 3467702== by 0x91DA9C: RelationBuildDesc (relcache.c:1200) > > which is complaining about the memory context identifiers for system > indexes' rd_indexcxt contexts. Those are surely not being leaked in > any real sense. I suspect that this has something to do with valgrind > not counting the context->ident fields as live pointers, but I don't > have enough valgrind-fu to fix that. Yea. I suspect it's related to the fact that we mark the memory as a valgrind mempool, I'll try to investigate. I do see a bunch of leaks bleats below fun:plpgsql_compile that I don't yet understand. E.g. ==2054558== 32 bytes in 1 blocks are definitely lost in loss record 284 of 913 ==2054558== at 0x89D389: palloc (mcxt.c:975) ==2054558== by 0x518732: new_list (list.c:134) ==2054558== by 0x518C0C: lappend (list.c:341) ==2054558== by 0x83CAE7: SearchCatCacheList (catcache.c:1691) ==2054558== by 0x859A9C: SearchSysCacheList (syscache.c:1447) ==2054558== by 0x313192: FuncnameGetCandidates (namespace.c:975) ==2054558== by 0x313D91: FunctionIsVisible (namespace.c:1450) ==2054558== by 0x7C2891: format_procedure_extended (regproc.c:375) ==2054558== by 0x7C27C3: format_procedure (regproc.c:324) ==2054558== by 0xA7693E1: do_compile (pl_comp.c:348) ==2054558== by 0xA769130: plpgsql_compile (pl_comp.c:224) and ==2054558== 30 bytes in 4 blocks are definitely lost in loss record 225 of 913 ==2054558== at 0x89D389: palloc (mcxt.c:975) ==2054558== by 0x3ADDAE: downcase_identifier (scansup.c:52) ==2054558== by 0x3ADD85: downcase_truncate_identifier (scansup.c:39) ==2054558== by 0x3AB5E4: core_yylex (scan.l:1032) ==2054558== by 0xA789B2D: internal_yylex (pl_scanner.c:321) ==2054558== by 0xA7896E3: plpgsql_yylex (pl_scanner.c:152) ==2054558== by 0xA780015: plpgsql_yyparse (pl_gram.c:1945) ==2054558== by 0xA76A652: do_compile (pl_comp.c:788) ==2054558== by 0xA769130: plpgsql_compile (pl_comp.c:224) ==2054558== by 0xA78948F: plpgsql_validator (pl_handler.c:539) Based on the quick look I had (dinner is calling) I didn't yet understand how plpgsql_compile_tmp_cxt error handling works. Greetings, Andres Freund (*) or not so quick, I had to figure out why valgrind was so slow. It turned out that I had typed shared_buffers=32MB into shared_buffers=32GB... -
Re: Getting better results from valgrind leak tracking
Zhihong Yu <zyu@yugabyte.com> — 2021-03-17T03:00:36Z
Hi, For the second last trace involving SearchCatCacheList (catcache.c:1691), the ctlist's members are stored in cl->members array where cl is returned at the end of SearchCatCacheList. Maybe this was not accounted for by valgrind ? Cheers On Tue, Mar 16, 2021 at 7:31 PM Andres Freund <andres@anarazel.de> wrote: > Hi, > > David, there's a question about a commit of yours below, hence adding > you. > > On 2021-03-16 19:36:10 -0400, Tom Lane wrote: > > Out of curiosity I poked at this for a little while. > > Cool. > > > > It doesn't appear to me that we leak much at all, at least not if you > > are willing to take "still reachable" blocks as not-leaked. > > Well, I think for any sort of automated testing - which I think would be > useful - we'd really need *no* leaks. I know that I get a few bleats > whenever I forget to set --leak-check=no. It's also not just postgres > itself, but some of the helper tools... > > And it's not just valgrind, also gcc/clang sanitizers... > > > > What I found out is that we have a lot of issues that seem to devolve > > to valgrind not being sure that a block is referenced. I identified > > two main causes of that: > > > > (1) We have a pointer, but it doesn't actually point right at the start > > of the block. A primary culprit here is lists of thingies that use the > > slist and dlist infrastructure. As an experiment, I moved the dlist_node > > fields of some popular structs to the beginning, and verified that that > > silences associated complaints. I'm not sure that we want to insist on > > put-the-link-first as policy (although if we did, it could provide some > > notational savings perhaps). However, unless someone knows of a way to > > teach valgrind about this situation, there may be no other way to silence > > those leakage complaints. A secondary culprit is people randomly > applying > > CACHELINEALIGN or the like to a palloc'd address, so that the address we > > have isn't pointing right at the block start. > > Hm, do you still have a backtrace / suppression for one of those? I > didn't see any in a quick (*) serial installcheck I just ran. Or I > wasn't able to pinpoint them to this issue. > > > I think the run might have shown a genuine leak: > > ==2048803== 16 bytes in 1 blocks are definitely lost in loss record 139 of > 906 > ==2048803== at 0x89D2EA: palloc (mcxt.c:975) > ==2048803== by 0x2392D3: heap_beginscan (heapam.c:1198) > ==2048803== by 0x264E8F: table_beginscan_strat (tableam.h:918) > ==2048803== by 0x265994: systable_beginscan (genam.c:453) > ==2048803== by 0x83C2D1: SearchCatCacheMiss (catcache.c:1359) > ==2048803== by 0x83C197: SearchCatCacheInternal (catcache.c:1299) > ==2048803== by 0x83BE9A: SearchCatCache1 (catcache.c:1167) > ==2048803== by 0x85876A: SearchSysCache1 (syscache.c:1134) > ==2048803== by 0x84CDB3: RelationInitTableAccessMethod (relcache.c:1795) > ==2048803== by 0x84F807: RelationBuildLocalRelation (relcache.c:3554) > ==2048803== by 0x303C9D: heap_create (heap.c:395) > ==2048803== by 0x305790: heap_create_with_catalog (heap.c:1291) > ==2048803== by 0x41A327: DefineRelation (tablecmds.c:885) > ==2048803== by 0x6C96B6: ProcessUtilitySlow (utility.c:1131) > ==2048803== by 0x6C948A: standard_ProcessUtility (utility.c:1034) > ==2048803== by 0x6C865F: ProcessUtility (utility.c:525) > ==2048803== by 0x6C7409: PortalRunUtility (pquery.c:1159) > ==2048803== by 0x6C7636: PortalRunMulti (pquery.c:1305) > ==2048803== by 0x6C6B11: PortalRun (pquery.c:779) > ==2048803== by 0x6C05AB: exec_simple_query (postgres.c:1173) > ==2048803== > { > <insert_a_suppression_name_here> > Memcheck:Leak > match-leak-kinds: definite > fun:palloc > fun:heap_beginscan > fun:table_beginscan_strat > fun:systable_beginscan > fun:SearchCatCacheMiss > fun:SearchCatCacheInternal > fun:SearchCatCache1 > fun:SearchSysCache1 > fun:RelationInitTableAccessMethod > fun:RelationBuildLocalRelation > fun:heap_create > fun:heap_create_with_catalog > fun:DefineRelation > fun:ProcessUtilitySlow > fun:standard_ProcessUtility > fun:ProcessUtility > fun:PortalRunUtility > fun:PortalRunMulti > fun:PortalRun > fun:exec_simple_query > } > > Since 56788d2156fc heap_beginscan() allocates > scan->rs_base.rs_private = > palloc(sizeof(ParallelBlockTableScanWorkerData)); > in heap_beginscan(). But doesn't free it in heap_endscan(). > > In most of the places heap scans are begun inside transient contexts, > but not always. In the above trace for example > RelationBuildLocalRelation switched to CacheMemoryContext, and nothing > switched to something else. > > I'm a bit confused about the precise design of rs_private / > ParallelBlockTableScanWorkerData, specifically why it's been added to > TableScanDesc, instead of just adding it to HeapScanDesc? And why is it > allocated unconditionally, instead of just for parallel scans? > > > I don't think this is a false positive, even though it theoretically > could be freed by resetting CacheMemoryContext (see below)? > > > I saw a lot of false positives from autovacuum workers, because > AutovacMemCxt is never deleted, and because table_toast_map is created > in TopMemoryContext. Adding an explicit > MemoryContextDelete(AutovacMemCxt) and parenting table_toast_map in that > shut that up. > > > Which brings me to the question why allocations in CacheMemoryContext, > AutovacMemCxt are considered to be "lost", even though they're still > "reachable" via a context reset. I think valgrind ends up treating > memory allocated via memory contexts that still exist at process end as > lost, regardless of being reachable via the the memory pool (from > valgrinds view). Which I guess actually makes sense, for things like > TopMemoryContext and CacheContext - anything not reachable by means > other than a context reset is effectively lost for those. > > For autovac launcher / worker it seems like a sensible thing to just > delete AutovacMemCxt. > > > > > (2) The only pointer to the start of a block is actually somewhere within > > the block. This is common in dynahash tables, where we allocate a slab > > of entries in a single palloc and then thread them together. Each entry > > should have exactly one referencing pointer, but that pointer is more > > likely to be elsewhere within the same palloc block than in the external > > hash bucket array. AFAICT, all cases except where the slab's first entry > > is pointed to by a hash bucket pointer confuse valgrind to some extent. > > I was able to hack around this by preventing dynahash from allocating > > more than one hash entry per palloc, but I wonder if there's a better > way. > > Hm. For me the number of leaks seem to stay the same with or without > your changes related to this. Is this a USE_VALGRIND build? > > I'm using valgrind-3.16.1 > > > > Attached is a very crude hack, not meant for commit, that hacks things > > up enough to greatly reduce the number of complaints with > > "--leak-check=full". > > > > One thing I've failed to silence so far is a bunch of entries like > > > > ==00:00:03:56.088 3467702== 1,861 bytes in 67 blocks are definitely lost > in loss record 1,290 of 1,418 > > ==00:00:03:56.088 3467702== at 0x950650: MemoryContextAlloc > (mcxt.c:827) > > ==00:00:03:56.088 3467702== by 0x951710: MemoryContextStrdup > (mcxt.c:1179) > > ==00:00:03:56.088 3467702== by 0x91C86E: RelationInitIndexAccessInfo > (relcache.c:1444) > > ==00:00:03:56.088 3467702== by 0x91DA9C: RelationBuildDesc > (relcache.c:1200) > > > > which is complaining about the memory context identifiers for system > > indexes' rd_indexcxt contexts. Those are surely not being leaked in > > any real sense. I suspect that this has something to do with valgrind > > not counting the context->ident fields as live pointers, but I don't > > have enough valgrind-fu to fix that. > > Yea. I suspect it's related to the fact that we mark the memory as a > valgrind mempool, I'll try to investigate. > > > I do see a bunch of leaks bleats below fun:plpgsql_compile that I don't > yet understand. E.g. > > ==2054558== 32 bytes in 1 blocks are definitely lost in loss record 284 of > 913 > ==2054558== at 0x89D389: palloc (mcxt.c:975) > ==2054558== by 0x518732: new_list (list.c:134) > ==2054558== by 0x518C0C: lappend (list.c:341) > ==2054558== by 0x83CAE7: SearchCatCacheList (catcache.c:1691) > ==2054558== by 0x859A9C: SearchSysCacheList (syscache.c:1447) > ==2054558== by 0x313192: FuncnameGetCandidates (namespace.c:975) > ==2054558== by 0x313D91: FunctionIsVisible (namespace.c:1450) > ==2054558== by 0x7C2891: format_procedure_extended (regproc.c:375) > ==2054558== by 0x7C27C3: format_procedure (regproc.c:324) > ==2054558== by 0xA7693E1: do_compile (pl_comp.c:348) > ==2054558== by 0xA769130: plpgsql_compile (pl_comp.c:224) > > and > > ==2054558== 30 bytes in 4 blocks are definitely lost in loss record 225 of > 913 > ==2054558== at 0x89D389: palloc (mcxt.c:975) > ==2054558== by 0x3ADDAE: downcase_identifier (scansup.c:52) > ==2054558== by 0x3ADD85: downcase_truncate_identifier (scansup.c:39) > ==2054558== by 0x3AB5E4: core_yylex (scan.l:1032) > ==2054558== by 0xA789B2D: internal_yylex (pl_scanner.c:321) > ==2054558== by 0xA7896E3: plpgsql_yylex (pl_scanner.c:152) > ==2054558== by 0xA780015: plpgsql_yyparse (pl_gram.c:1945) > ==2054558== by 0xA76A652: do_compile (pl_comp.c:788) > ==2054558== by 0xA769130: plpgsql_compile (pl_comp.c:224) > ==2054558== by 0xA78948F: plpgsql_validator (pl_handler.c:539) > > Based on the quick look I had (dinner is calling) I didn't yet > understand how plpgsql_compile_tmp_cxt error handling works. > > Greetings, > > Andres Freund > > > (*) or not so quick, I had to figure out why valgrind was so slow. It > turned out that I had typed shared_buffers=32MB into > shared_buffers=32GB... > > > -
Re: Getting better results from valgrind leak tracking
Tom Lane <tgl@sss.pgh.pa.us> — 2021-03-17T03:01:47Z
Andres Freund <andres@anarazel.de> writes: > On 2021-03-16 19:36:10 -0400, Tom Lane wrote: >> It doesn't appear to me that we leak much at all, at least not if you >> are willing to take "still reachable" blocks as not-leaked. > Well, I think for any sort of automated testing - which I think would be > useful - we'd really need *no* leaks. That seems both unnecessary and impractical. We have to consider that everything-still-reachable is an OK final state. > I think the run might have shown a genuine leak: > ==2048803== 16 bytes in 1 blocks are definitely lost in loss record 139 of 906 > ==2048803== at 0x89D2EA: palloc (mcxt.c:975) > ==2048803== by 0x2392D3: heap_beginscan (heapam.c:1198) > ==2048803== by 0x264E8F: table_beginscan_strat (tableam.h:918) > ==2048803== by 0x265994: systable_beginscan (genam.c:453) > ==2048803== by 0x83C2D1: SearchCatCacheMiss (catcache.c:1359) > ==2048803== by 0x83C197: SearchCatCacheInternal (catcache.c:1299) I didn't see anything like that after applying the fixes I showed before. There are a LOT of false positives from the fact that with our HEAD code, valgrind believes that everything in the catalog caches and most things in dynahash tables (including the relcache) are unreachable. I'm not trying to claim there are no leaks anywhere, just that the amount of noise from those issues swamps all the real problems. I particularly recommend not believing anything related to catcache or relcache if you haven't applied that admittedly-hacky patch. > Hm. For me the number of leaks seem to stay the same with or without > your changes related to this. Is this a USE_VALGRIND build? Yeah ... > I do see a bunch of leaks bleats below fun:plpgsql_compile that I don't > yet understand. E.g. Those are probably a variant of what you were suggesting above, ie plpgsql isn't terribly careful not to leak random stuff while building a long-lived function parse tree. It's supposed to use a temp context for anything that might leak, but I suspect it's not thorough about it. We could chase that sort of thing after we clean up the other problems. regards, tom lane
-
Re: Getting better results from valgrind leak tracking
Tom Lane <tgl@sss.pgh.pa.us> — 2021-03-17T03:23:42Z
Andres Freund <andres@anarazel.de> writes: > Hm. For me the number of leaks seem to stay the same with or without > your changes related to this. Is this a USE_VALGRIND build? Not sure how you arrived at that answer. I attach two logs of individual backends running with --leak-check=full --track-origins=yes --read-var-info=yes --error-exitcode=0 The test scenario in both cases was just start up, run "select 2+2;", quit. The first one is before I'd made any of the changes shown before, the second is after. regards, tom lane
-
Re: Getting better results from valgrind leak tracking
Andres Freund <andres@anarazel.de> — 2021-03-17T03:50:17Z
Hi, On Tue, Mar 16, 2021, at 20:01, Tom Lane wrote: > Andres Freund <andres@anarazel.de> writes: > > On 2021-03-16 19:36:10 -0400, Tom Lane wrote: > >> It doesn't appear to me that we leak much at all, at least not if you > >> are willing to take "still reachable" blocks as not-leaked. > > > Well, I think for any sort of automated testing - which I think would be > > useful - we'd really need *no* leaks. > > That seems both unnecessary and impractical. We have to consider that > everything-still-reachable is an OK final state. I don't consider "still reachable" a leak. Just definitely unreachable. And with a few tweaks that seems like we could achieve that? > > I think the run might have shown a genuine leak: > > > ==2048803== 16 bytes in 1 blocks are definitely lost in loss record 139 of 906 > > ==2048803== at 0x89D2EA: palloc (mcxt.c:975) > > ==2048803== by 0x2392D3: heap_beginscan (heapam.c:1198) > > ==2048803== by 0x264E8F: table_beginscan_strat (tableam.h:918) > > ==2048803== by 0x265994: systable_beginscan (genam.c:453) > > ==2048803== by 0x83C2D1: SearchCatCacheMiss (catcache.c:1359) > > ==2048803== by 0x83C197: SearchCatCacheInternal (catcache.c:1299) > > I didn't see anything like that after applying the fixes I showed before. > There are a LOT of false positives from the fact that with our HEAD > code, valgrind believes that everything in the catalog caches and > most things in dynahash tables (including the relcache) are unreachable. I think it's actually unreachable memory (unless you count resetting the cache context), based on manually tracing the code... I'll try to repro. > > I do see a bunch of leaks bleats below fun:plpgsql_compile that I don't > > yet understand. E.g. > > Those are probably a variant of what you were suggesting above, ie > plpgsql isn't terribly careful not to leak random stuff while building > a long-lived function parse tree. It's supposed to use a temp context > for anything that might leak, but I suspect it's not thorough about it. What I meant was that I didn't understand how there's not a leak danger when compilation fails halfway through, given that the context in question is below TopMemoryContext and that I didn't see a relevant TRY block. But that probably there is something cleaning it up that I didn't see. Andres
-
Re: Getting better results from valgrind leak tracking
Tom Lane <tgl@sss.pgh.pa.us> — 2021-03-17T04:01:55Z
"Andres Freund" <andres@anarazel.de> writes: > On Tue, Mar 16, 2021, at 20:01, Tom Lane wrote: >> That seems both unnecessary and impractical. We have to consider that >> everything-still-reachable is an OK final state. > I don't consider "still reachable" a leak. Just definitely unreachable. OK, we're in violent agreement then -- I must've misread what you wrote. >>> I think the run might have shown a genuine leak: >> I didn't see anything like that after applying the fixes I showed before. > I think it's actually unreachable memory (unless you count resetting the cache context), based on manually tracing the code... I'll try to repro. I agree that having to reset CacheContext is not something we should count as "still reachable", and I'm pretty sure that the existing valgrind infrastructure doesn't count it that way. As for the particular point about ParallelBlockTableScanWorkerData, I agree with your question to David about why that's in TableScanDesc not HeapScanDesc, but I can't get excited about it not being freed in heap_endscan. That's mainly because I do not believe that anything as complex as a heap or indexscan should be counted on to be zero-leakage. The right answer is to not do such operations in long-lived contexts. So if we're running such a thing while switched into CacheContext, *that* is the bug, not that heap_endscan didn't free this particular allocation. regards, tom lane
-
Re: Getting better results from valgrind leak tracking
Andres Freund <andres@anarazel.de> — 2021-03-17T05:57:18Z
Hi, On 2021-03-16 20:50:17 -0700, Andres Freund wrote: > What I meant was that I didn't understand how there's not a leak > danger when compilation fails halfway through, given that the context > in question is below TopMemoryContext and that I didn't see a relevant > TRY block. But that probably there is something cleaning it up that I > didn't see. Looks like it's an actual leak: postgres[2058957][1]=# DO $do$BEGIN CREATE OR REPLACE FUNCTION foo() RETURNS VOID LANGUAGE plpgsql AS $$BEGIN frakbar;END;$$;^C postgres[2058957][1]=# SELECT count(*), SUM(total_bytes) FROM pg_backend_memory_contexts WHERE name = 'PL/pgSQL function'; ┌───────┬────────┐ │ count │ sum │ ├───────┼────────┤ │ 0 │ (null) │ └───────┴────────┘ (1 row) Time: 1.666 ms postgres[2058957][1]=# CREATE OR REPLACE FUNCTION foo() RETURNS VOID LANGUAGE plpgsql AS $$BEGIN frakbar;END;$$; ERROR: 42601: syntax error at or near "frakbar" LINE 1: ...ON foo() RETURNS VOID LANGUAGE plpgsql AS $$BEGIN frakbar;EN... ^ LOCATION: scanner_yyerror, scan.l:1176 Time: 5.463 ms postgres[2058957][1]=# CREATE OR REPLACE FUNCTION foo() RETURNS VOID LANGUAGE plpgsql AS $$BEGIN frakbar;END;$$; ERROR: 42601: syntax error at or near "frakbar" LINE 1: ...ON foo() RETURNS VOID LANGUAGE plpgsql AS $$BEGIN frakbar;EN... ^ LOCATION: scanner_yyerror, scan.l:1176 Time: 1.223 ms postgres[2058957][1]=# CREATE OR REPLACE FUNCTION foo() RETURNS VOID LANGUAGE plpgsql AS $$BEGIN frakbar;END;$$; ERROR: 42601: syntax error at or near "frakbar" LINE 1: ...ON foo() RETURNS VOID LANGUAGE plpgsql AS $$BEGIN frakbar;EN... ^ LOCATION: scanner_yyerror, scan.l:1176 Time: 1.194 ms postgres[2058957][1]=# SELECT count(*), SUM(total_bytes) FROM pg_backend_memory_contexts WHERE name = 'PL/pgSQL function'; ┌───────┬───────┐ │ count │ sum │ ├───────┼───────┤ │ 3 │ 24576 │ └───────┴───────┘ (1 row) Something like DO $do$ BEGIN FOR i IN 1 .. 10000 LOOP BEGIN EXECUTE $cf$CREATE OR REPLACE FUNCTION foo() RETURNS VOID LANGUAGE plpgsql AS $f$BEGIN frakbar; END;$f$;$cf$; EXCEPTION WHEN others THEN END; END LOOP; END;$do$; will show the leak visible in top too (albeit slowly - a more complicated statement will leak more quickly I think). postgres[2059268][1]=# SELECT count(*), SUM(total_bytes) FROM pg_backend_memory_contexts WHERE name = 'PL/pgSQL function'; ┌───────┬──────────┐ │ count │ sum │ ├───────┼──────────┤ │ 10000 │ 81920000 │ └───────┴──────────┘ (1 row) The leak appears to be not new, I see it in 9.6 as well. This seems like a surprisingly easy to trigger leak... Looks like there's something else awry. The above DO statement takes 2.2s on an 13 assert build, but 32s on a master assert build. Spending a lot of time doing dependency lookups: - 94.62% 0.01% postgres postgres [.] CreateFunction - 94.61% CreateFunction - 94.56% ProcedureCreate - 89.68% deleteDependencyRecordsFor - 89.38% systable_getnext - 89.33% index_getnext_slot - 56.00% index_fetch_heap + 54.64% table_index_fetch_tuple 0.09% heapam_index_fetch_tuple + 28.53% index_getnext_tid 2.77% ItemPointerEquals 0.10% table_index_fetch_tuple 0.09% btgettuple 0.03% index_getnext_tid 1000 iterations: 521ms 1000 iterations: 533ms 2000 iterations: 1670ms 3000 iterations: 3457ms 3000 iterations: 3457ms 10000 iterations: 31794ms The quadratic seeming nature made me wonder if someone broke killtuples in this situation. And it seem that someone was me, in 623a9ba . We need to bump xactCompletionCount in the subxid abort case as well... Greetings, Andres Freund -
Re: Getting better results from valgrind leak tracking
Tom Lane <tgl@sss.pgh.pa.us> — 2021-03-17T14:16:44Z
Andres Freund <andres@anarazel.de> writes: > On 2021-03-16 20:50:17 -0700, Andres Freund wrote: >> What I meant was that I didn't understand how there's not a leak >> danger when compilation fails halfway through, given that the context >> in question is below TopMemoryContext and that I didn't see a relevant >> TRY block. But that probably there is something cleaning it up that I >> didn't see. > Looks like it's an actual leak: Yeah, I believe that. On the other hand, I'm not sure that such cases represent any real problem for production usage. I'm inclined to focus on non-error scenarios first. (Having said that, we probably have the ability to fix such things relatively painlessly now, by reparenting an initially-temporary context once we're done parsing.) Meanwhile, I'm still trying to understand why valgrind is whining about the rd_indexcxt identifier strings. AFAICS it shouldn't. regards, tom lane
-
Re: Getting better results from valgrind leak tracking
Andres Freund <andres@anarazel.de> — 2021-03-17T15:15:43Z
Hi, On Wed, Mar 17, 2021, at 07:16, Tom Lane wrote: > Andres Freund <andres@anarazel.de> writes: > > On 2021-03-16 20:50:17 -0700, Andres Freund wrote: > Meanwhile, I'm still trying to understand why valgrind is whining > about the rd_indexcxt identifier strings. AFAICS it shouldn't. I found a way around that late last night. Need to mark the context itself as an allocation. But I made a mess on the way to that and need to clean the patch up before sending it (and need to drop my girlfriend off first). Andres
-
Re: Getting better results from valgrind leak tracking
Andres Freund <andres@anarazel.de> — 2021-03-17T18:15:31Z
Hi, (really need to fix my mobile phone mail program to keep the CC list...) On 2021-03-17 08:15:43 -0700, Andres Freund wrote: > On Wed, Mar 17, 2021, at 07:16, Tom Lane wrote: > > Andres Freund <andres@anarazel.de> writes: > > > On 2021-03-16 20:50:17 -0700, Andres Freund wrote: > > Meanwhile, I'm still trying to understand why valgrind is whining > > about the rd_indexcxt identifier strings. AFAICS it shouldn't. > > I found a way around that late last night. Need to mark the context > itself as an allocation. But I made a mess on the way to that and need > to clean the patch up before sending it (and need to drop my > girlfriend off first). Unfortunately I didn't immediately find a way to do this while keeping the MEMPOOL_CREATE/DESTROY in mcxt.c. The attached patch moves the pool creation into the memory context implementations, "allocates" the context itself as part of that pool, and changes the reset implementation from MEMPOOL_DESTROY + MEMPOOL_CREATE to instead do MEMPOOL_TRIM. That leaves the memory context itself valid (and thus tracking ->ident etc), but marks all the other memory as freed. This is just a first version, it probably needs more work, and definitely a few comments... After this, your changes, and the previously mentioned fixes, I get far fewer false positives. Also found a crash / memory leak in pgstat.c due to the new replication slot stats, but I'll start a separate thread. There are a few leak warnings around guc.c that look like they might be real, not false positives, and thus a bit concerning. Looks like several guc check hooks don't bother to free the old *extra before allocating a new one. I suspect we might get better results from valgrind, not just for leaks but also undefined value tracking, if we changed the way we represent pools to utilize VALGRIND_MEMPOOL_METAPOOL | VALGRIND_MEMPOOL_AUTO_FREE. E.g. aset.c would associate AllocBlock using VALGRIND_MEMPOOL_ALLOC and then mcxt.c would use VALGRIND_MALLOCLIKE_BLOCK for the individual chunk allocation. https://www.valgrind.org/docs/manual/mc-manual.html#mc-manual.mempools I played with naming the allocations underlying aset.c using VALGRIND_CREATE_BLOCK(block, strlen(context->name), context->name). That does produce better undefined-value warnings, but it seems that e.g. the leak detector doen't have that information around. Nor does it seem to be usable for use-afte-free. At least the latter likely because I had to VALGRIND_DISCARD by that point... Greetings, Andres Freund
-
Re: Getting better results from valgrind leak tracking
Tom Lane <tgl@sss.pgh.pa.us> — 2021-03-17T22:07:36Z
Andres Freund <andres@anarazel.de> writes: >> I found a way around that late last night. Need to mark the context >> itself as an allocation. But I made a mess on the way to that and need >> to clean the patch up before sending it (and need to drop my >> girlfriend off first). > Unfortunately I didn't immediately find a way to do this while keeping > the MEMPOOL_CREATE/DESTROY in mcxt.c. The attached patch moves the pool > creation into the memory context implementations, "allocates" the > context itself as part of that pool, and changes the reset > implementation from MEMPOOL_DESTROY + MEMPOOL_CREATE to instead do > MEMPOOL_TRIM. That leaves the memory context itself valid (and thus > tracking ->ident etc), but marks all the other memory as freed. Huh, interesting. I wonder why that makes the ident problem go away? I'd supposed that valgrind would see the context headers as ordinary memory belonging to the global "malloc" pool, so that any pointers inside them ought to be considered valid. Anyway, I don't have a problem with rearranging the responsibility like this. It gives the individual allocators more freedom to do odd stuff, at the cost of very minor duplication of valgrind calls. I agree we need more comments -- would you like me to have a go at writing them? One thing I was stewing over last night is that a MemoryContextReset will mess up any context identifier assigned with MemoryContextCopyAndSetIdentifier. I'd left that as a problem to fix later, because we don't currently have a need to reset contexts that use copied identifiers. But that assumption obviously will bite us someday, so maybe now is a good time to think about it. The very simplest fix would be to allocate non-constant idents with malloc; which'd require adding a flag to track whether context->ident needs to be free()d. We have room for another bool near the top of struct MemoryContextData (and at some point we could turn those bool fields into a flags word). The only real cost here is one more free() while destroying a labeled context, which is probably negligible. Other ideas are possible but they seem to require getting the individual mcxt methods involved, and I doubt it's worth the complexity. > There are a few leak warnings around guc.c that look like they might be > real, not false positives, and thus a bit concerning. Looks like several > guc check hooks don't bother to free the old *extra before allocating a > new one. I'll take a look, but I'm pretty certain that guc.c, not the hooks, is responsible for freeing those. Might be another case of valgrind not understanding what's happening. > I suspect we might get better results from valgrind, not just for leaks > but also undefined value tracking, if we changed the way we represent > pools to utilize VALGRIND_MEMPOOL_METAPOOL | > VALGRIND_MEMPOOL_AUTO_FREE. Don't really see why that'd help? I mean, it could conceivably help catch bugs in the allocators themselves, but I don't follow the argument that it'd improve anything else. Defined is defined, as far as I can tell from the valgrind manual. regards, tom lane
-
Re: Getting better results from valgrind leak tracking
Andres Freund <andres@anarazel.de> — 2021-03-18T00:26:56Z
Hi, On 2021-03-17 18:07:36 -0400, Tom Lane wrote: > Andres Freund <andres@anarazel.de> writes: > >> I found a way around that late last night. Need to mark the context > >> itself as an allocation. But I made a mess on the way to that and need > >> to clean the patch up before sending it (and need to drop my > >> girlfriend off first). > > > Unfortunately I didn't immediately find a way to do this while keeping > > the MEMPOOL_CREATE/DESTROY in mcxt.c. The attached patch moves the pool > > creation into the memory context implementations, "allocates" the > > context itself as part of that pool, and changes the reset > > implementation from MEMPOOL_DESTROY + MEMPOOL_CREATE to instead do > > MEMPOOL_TRIM. That leaves the memory context itself valid (and thus > > tracking ->ident etc), but marks all the other memory as freed. > > Huh, interesting. I wonder why that makes the ident problem go away? > I'd supposed that valgrind would see the context headers as ordinary > memory belonging to the global "malloc" pool, so that any pointers > inside them ought to be considered valid. I'm didn't quite understand either at the time of writing the change. It just seemed the only explanation for some the behaviour I was seeing, so I tried it. Just to be initially be rebuffed due to errors when accessing the recycled sets... I spent a bit of time looking at valgrind, and it looks to be explicit behaviour: memcheck/mc_leakcheck.c static MC_Chunk** find_active_chunks(Int* pn_chunks) { // Our goal is to construct a set of chunks that includes every // mempool chunk, and every malloc region that *doesn't* contain a // mempool chunk. ... // Then we loop over the mempool tables. For each chunk in each // pool, we set the entry in the Bool array corresponding to the // malloc chunk containing the mempool chunk. VG_(HT_ResetIter)(MC_(mempool_list)); while ( (mp = VG_(HT_Next)(MC_(mempool_list))) ) { VG_(HT_ResetIter)(mp->chunks); while ( (mc = VG_(HT_Next)(mp->chunks)) ) { // We'll need to record this chunk. n_chunks++; // Possibly invalidate the malloc holding the beginning of this chunk. m = find_chunk_for(mc->data, mallocs, n_mallocs); if (m != -1 && malloc_chunk_holds_a_pool_chunk[m] == False) { tl_assert(n_chunks > 0); n_chunks--; malloc_chunk_holds_a_pool_chunk[m] = True; } I think that means it explicitly ignores the entire malloced allocation whenever there's *any* mempool chunk in it, instead considering only the mempool chunks. So once aset allocats something in the init block, the context itself is ignored for leak checking purposes. But that wouldn't be the case if we didn't have the init block... I guess that makes sense, but would definitely be nice to have had documented... > Anyway, I don't have a problem with rearranging the responsibility > like this. It gives the individual allocators more freedom to do > odd stuff, at the cost of very minor duplication of valgrind calls. Yea, similar. > I agree we need more comments -- would you like me to have a go at > writing them? Gladly! > One thing I was stewing over last night is that a MemoryContextReset > will mess up any context identifier assigned with > MemoryContextCopyAndSetIdentifier. Valgrind should catch such problems. Not having the danger would be better, of course. We could also add an assertion at reset time that the identifier isn't in the current context. > The very simplest fix would be to allocate non-constant idents with > malloc; which'd require adding a flag to track whether context->ident > needs to be free()d. We have room for another bool near the top of > struct MemoryContextData (and at some point we could turn those > bool fields into a flags word). The only real cost here is one > more free() while destroying a labeled context, which is probably > negligible. Hm. A separate malloc()/free() could conceivably actually show up in profiles at some point. What if we instead used that flag to indicate that MemoryContextReset() needs to save the identifier? Then any cost would only be paid if the context is actually reset. > > There are a few leak warnings around guc.c that look like they might be > > real, not false positives, and thus a bit concerning. Looks like several > > guc check hooks don't bother to free the old *extra before allocating a > > new one. > > I'll take a look, but I'm pretty certain that guc.c, not the hooks, is > responsible for freeing those. Yea, I had misattributed the leak to the callbacks. One of the things I saw definitely is a leak: if call_string_check_hook() ereport(ERRORs) the guc_strdup() of newval->stringval is lost. There's another set of them that seems to be related to paralellism. But I've not hunted it down yet. I think it might be worth adding a VALGRIND_DO_CHANGED_LEAK_CHECK() at the end of a transaction or such? That way it'd not be quite as hard to pinpoint the source of a leak to individual statements as it is right now. > Might be another case of valgrind not understanding what's happening. Those allocations seem very straightforward, plain malloc/free that is referenced by a pointer to the start of the allocation. So that doesn't seem very likely? > > I suspect we might get better results from valgrind, not just for leaks > > but also undefined value tracking, if we changed the way we represent > > pools to utilize VALGRIND_MEMPOOL_METAPOOL | > > VALGRIND_MEMPOOL_AUTO_FREE. > > Don't really see why that'd help? I mean, it could conceivably help > catch bugs in the allocators themselves, but I don't follow the argument > that it'd improve anything else. Defined is defined, as far as I can > tell from the valgrind manual. I think it might improve the attribution of some use-after-free errors to the memory context. Looks like it also might reduce the cost of running valgrind a bit. Greetings, Andres Freund -
Re: Getting better results from valgrind leak tracking
Tom Lane <tgl@sss.pgh.pa.us> — 2021-03-18T01:30:48Z
Andres Freund <andres@anarazel.de> writes: > On 2021-03-17 18:07:36 -0400, Tom Lane wrote: >> Huh, interesting. I wonder why that makes the ident problem go away? > I spent a bit of time looking at valgrind, and it looks to be explicit > behaviour: > > // Our goal is to construct a set of chunks that includes every > // mempool chunk, and every malloc region that *doesn't* contain a > // mempool chunk. Ugh. > I guess that makes sense, but would definitely be nice to have had > documented... Indeed. So this started happening to us when we merged the aset header with its first allocation block. >>> There are a few leak warnings around guc.c that look like they might be >>> real, not false positives, and thus a bit concerning. Looks like several >>> guc check hooks don't bother to free the old *extra before allocating a >>> new one. >> I'll take a look, but I'm pretty certain that guc.c, not the hooks, is >> responsible for freeing those. I believe I've just tracked down the cause of that. Those errors are (as far as I've seen) only happening in parallel workers, and the reason is this gem in RestoreGUCState: /* See comment at can_skip_gucvar(). */ for (i = 0; i < num_guc_variables; i++) if (!can_skip_gucvar(guc_variables[i])) InitializeOneGUCOption(guc_variables[i]); where InitializeOneGUCOption zeroes out that GUC variable, causing it to lose track of any allocations it was responsible for. At minimum, somebody didn't understand the difference between "initialize" and "reset". But I suspect we could just nuke this loop altogether. I've not yet tried to grok the comment that purports to justify it, but I fail to see why it'd ever be useful to drop values inherited from the postmaster. regards, tom lane
-
Re: Getting better results from valgrind leak tracking
Andres Freund <andres@anarazel.de> — 2021-03-18T02:05:19Z
Hi, On 2021-03-17 21:30:48 -0400, Tom Lane wrote: > Andres Freund <andres@anarazel.de> writes: > > On 2021-03-17 18:07:36 -0400, Tom Lane wrote: > >> Huh, interesting. I wonder why that makes the ident problem go away? > > > I spent a bit of time looking at valgrind, and it looks to be explicit > > behaviour: > > > > // Our goal is to construct a set of chunks that includes every > > // mempool chunk, and every malloc region that *doesn't* contain a > > // mempool chunk. > > Ugh. > > > I guess that makes sense, but would definitely be nice to have had > > documented... > > Indeed. So this started happening to us when we merged the aset > header with its first allocation block. Yea. I have the feeling that valgrinds error detection capability in our codebase used to be higher too. I wonder if that could be related somehow. Or maybe it's just a figment of my imagination. > I believe I've just tracked down the cause of that. Those errors > are (as far as I've seen) only happening in parallel workers, and > the reason is this gem in RestoreGUCState: > > /* See comment at can_skip_gucvar(). */ > for (i = 0; i < num_guc_variables; i++) > if (!can_skip_gucvar(guc_variables[i])) > InitializeOneGUCOption(guc_variables[i]); > > where InitializeOneGUCOption zeroes out that GUC variable, causing > it to lose track of any allocations it was responsible for. Ouch. At least it's a short lived issue rather than something permanently leaking memory on every SIGHUP... > At minimum, somebody didn't understand the difference between "initialize" > and "reset". But I suspect we could just nuke this loop altogether. > I've not yet tried to grok the comment that purports to justify it, > but I fail to see why it'd ever be useful to drop values inherited > from the postmaster. I can't really make sense of it either. I think it may be trying to restore the GUC state to what it would have been in postmaster, disregarding all the settings that were set as part of PostgresInit() etc? Greetings, Andres Freund
-
Re: Getting better results from valgrind leak tracking
Tom Lane <tgl@sss.pgh.pa.us> — 2021-03-18T02:33:59Z
Andres Freund <andres@anarazel.de> writes: > On 2021-03-17 21:30:48 -0400, Tom Lane wrote: >> I believe I've just tracked down the cause of that. Those errors >> are (as far as I've seen) only happening in parallel workers, and >> the reason is this gem in RestoreGUCState: ... > Ouch. At least it's a short lived issue rather than something permanently > leaking memory on every SIGHUP... Yeah. This leak is really insignificant in practice, although I'm suspicious that randomly init'ing GUCs like this might have semantic issues that we've not detected yet. >> I've not yet tried to grok the comment that purports to justify it, >> but I fail to see why it'd ever be useful to drop values inherited >> from the postmaster. > I can't really make sense of it either. I think it may be trying to > restore the GUC state to what it would have been in postmaster, > disregarding all the settings that were set as part of PostgresInit() > etc? At least in a non-EXEC_BACKEND build, the most reliable way to reproduce the postmaster's settings is to do nothing whatsoever. And I think the same is true for EXEC_BACKEND, really, because the guc.c subsystem is responsible for restoring what would have been the inherited-via-fork settings. So I'm really not sure what this is on about, and I'm too tired to try to figure it out tonight. In other news, I found that there's a genuine leak in RelationBuildLocalRelation: RelationInitTableAccessMethod was added in a spot where CurrentMemoryContext is CacheMemoryContext, which is bad because it does a syscache lookup that can lead to a table access which can leak some memory. Seems easy to fix though. The plpgsql situation looks like a mess. As a short-term answer, I'm inclined to recommend adding an exclusion that will ignore anything allocated within plpgsql_compile(). Doing better will require a fair amount of rewriting. (Although I suppose we could also consider adding an on_proc_exit callback that runs through and frees all the function cache entries.) regards, tom lane
-
Re: Getting better results from valgrind leak tracking
Andres Freund <andres@anarazel.de> — 2021-03-18T03:02:50Z
Hi, On 2021-03-17 00:01:55 -0400, Tom Lane wrote: > As for the particular point about ParallelBlockTableScanWorkerData, > I agree with your question to David about why that's in TableScanDesc > not HeapScanDesc, but I can't get excited about it not being freed in > heap_endscan. That's mainly because I do not believe that anything as > complex as a heap or indexscan should be counted on to be zero-leakage. > The right answer is to not do such operations in long-lived contexts. > So if we're running such a thing while switched into CacheContext, > *that* is the bug, not that heap_endscan didn't free this particular > allocation. I agree that it's a bad idea to do scans in non-transient contexts. It does however seems like there's a number of places that do... I added the following hacky definition of "permanent" contexts /* * NB: Only for assertion use. * * TopMemoryContext itself obviously is permanent. Treat CacheMemoryContext * and all its children as permanent too. * * XXX: Might be worth adding this as an explicit flag on the context? */ bool MemoryContextIsPermanent(MemoryContext c) { if (c == TopMemoryContext) return true; while (c) { if (c == CacheMemoryContext) return true; c = c->parent; } return false; } and checked that the CurrentMemoryContext is not permanent in SearchCatCacheInternal() and systable_beginscan(). Hit a number of times. The most glaring case is the RelationInitTableAccessMethod() call in RelationBuildLocalRelation(). Seems like the best fix is to just move the MemoryContextSwitchTo() to just before the RelationInitTableAccessMethod(). Although I wonder if we shouldn't go further, and move it to much earlier, somewhere after the rd_rel allocation. There's plenty other hits, but I think I should get back to working on making the shared memory stats patch committable. I really wouldn't want it to slip yet another year. But I think it might make sense to add a flag indicating contexts that shouldn't be used for non-transient data. Seems like we fairly regularly have "bugs" around this? Greetings, Andres Freund -
Re: Getting better results from valgrind leak tracking
Andres Freund <andres@anarazel.de> — 2021-03-18T03:15:36Z
On 2021-03-17 22:33:59 -0400, Tom Lane wrote: > >> I've not yet tried to grok the comment that purports to justify it, > >> but I fail to see why it'd ever be useful to drop values inherited > >> from the postmaster. > > > I can't really make sense of it either. I think it may be trying to > > restore the GUC state to what it would have been in postmaster, > > disregarding all the settings that were set as part of PostgresInit() > > etc? > > At least in a non-EXEC_BACKEND build, the most reliable way to reproduce > the postmaster's settings is to do nothing whatsoever. And I think the > same is true for EXEC_BACKEND, really, because the guc.c subsystem is > responsible for restoring what would have been the inherited-via-fork > settings. So I'm really not sure what this is on about, and I'm too > tired to try to figure it out tonight. The restore thing runs after we've already set and initialized GUCs, including things like user/database default GUCs. Is see things like ==2251779== 4,560 bytes in 1 blocks are definitely lost in loss record 383 of 406 ==2251779== at 0x483877F: malloc (vg_replace_malloc.c:307) ==2251779== by 0x714A45: ConvertTimeZoneAbbrevs (datetime.c:4556) ==2251779== by 0x88DE95: load_tzoffsets (tzparser.c:465) ==2251779== by 0x88511D: check_timezone_abbreviations (guc.c:11565) ==2251779== by 0x884633: call_string_check_hook (guc.c:11232) ==2251779== by 0x87CB57: parse_and_validate_value (guc.c:7012) ==2251779== by 0x87DD5F: set_config_option (guc.c:7630) ==2251779== by 0x88397F: ProcessGUCArray (guc.c:10784) ==2251779== by 0x32BCCF: ApplySetting (pg_db_role_setting.c:256) ==2251779== by 0x874CA2: process_settings (postinit.c:1163) ==2251779== by 0x874A0B: InitPostgres (postinit.c:1048) ==2251779== by 0x60129A: BackgroundWorkerInitializeConnectionByOid (postmaster.c:5681) ==2251779== by 0x2BB283: ParallelWorkerMain (parallel.c:1374) ==2251779== by 0x5EBA6A: StartBackgroundWorker (bgworker.c:864) ==2251779== by 0x6014FE: do_start_bgworker (postmaster.c:5802) ==2251779== by 0x6018D2: maybe_start_bgworkers (postmaster.c:6027) ==2251779== by 0x600811: sigusr1_handler (postmaster.c:5190) ==2251779== by 0x4DD513F: ??? (in /usr/lib/x86_64-linux-gnu/libpthread-2.31.so) ==2251779== by 0x556E865: select (select.c:41) ==2251779== by 0x5FC0CB: ServerLoop (postmaster.c:1700) ==2251779== by 0x5FBA68: PostmasterMain (postmaster.c:1408) ==2251779== by 0x4F8BFD: main (main.c:209) The BackgroundWorkerInitializeConnectionByOid() in that trace is before the RestoreGUCState() later in ParallelWorkerMain(). But that doesn't really explain the approach. > In other news, I found that there's a genuine leak in > RelationBuildLocalRelation: RelationInitTableAccessMethod > was added in a spot where CurrentMemoryContext is CacheMemoryContext, > which is bad because it does a syscache lookup that can lead to > a table access which can leak some memory. Seems easy to fix though. Yea, that's the one I was talking about re ParallelBlockTableScanWorkerData not being freed. While I think we should also add that pfree, I think you're right, and we shouldn't do RelationInitTableAccessMethod() while in CacheMemoryContext. > The plpgsql situation looks like a mess. As a short-term answer, > I'm inclined to recommend adding an exclusion that will ignore anything > allocated within plpgsql_compile(). Doing better will require a fair > amount of rewriting. (Although I suppose we could also consider adding > an on_proc_exit callback that runs through and frees all the function > cache entries.) The error variant of this one seems like it might actually be a practically relevant leak? As well as increasing memory usage for compiled plpgsql functions unnecessarily, of course. The latter would be good to fix, but the former seems like it might be a practical issue for poolers and the like? So I think we should do at least the reparenting thing to address that? Greetings, Andres Freund
-
Re: Getting better results from valgrind leak tracking
Tom Lane <tgl@sss.pgh.pa.us> — 2021-03-18T03:21:47Z
Andres Freund <andres@anarazel.de> writes: > The most glaring case is the RelationInitTableAccessMethod() call in > RelationBuildLocalRelation(). Seems like the best fix is to just move > the MemoryContextSwitchTo() to just before the > RelationInitTableAccessMethod(). Although I wonder if we shouldn't go > further, and move it to much earlier, somewhere after the rd_rel > allocation. Yeah, same thing I did locally. Not sure if it's worth working harder. > There's plenty other hits, but I think I should get back to working on > making the shared memory stats patch committable. I really wouldn't want > it to slip yet another year. +1, so far there's little indication that we're finding any serious leaks here. Might be best to set it all aside till there's more time. regards, tom lane
-
Re: Getting better results from valgrind leak tracking
Tom Lane <tgl@sss.pgh.pa.us> — 2021-03-18T21:51:28Z
I wrote: > Andres Freund <andres@anarazel.de> writes: >> There's plenty other hits, but I think I should get back to working on >> making the shared memory stats patch committable. I really wouldn't want >> it to slip yet another year. > +1, so far there's little indication that we're finding any serious leaks > here. Might be best to set it all aside till there's more time. Well, I failed to resist the temptation to keep poking at this issue, mainly because I felt that it'd be a good idea to make sure we'd gotten our arms around all of the detectable issues, even if we choose not to fix them right away. The attached patch, combined with your preceding memory context patch, eliminates all but a very small number of the leak complaints in the core regression tests. The remaing leak warnings that I see are: 1. WaitForReplicationWorkerAttach leaks the BackgroundWorkerHandle it's passed. I'm not sure which function should clean that up, but in any case it's only 16 bytes one time in one process, so it's hardly exciting. 2. There's lots of leakage in text search dictionary initialization functions. This is hard to get around completely, because the API for those functions has them being called in the dictionary's long-lived context. In any case, the leaks are not large (modulo comments below), and they would get cleaned up if the dictionary cache were thrown away. 3. partition_prune.sql repeatably produces this warning: ==00:00:44:39.764 3813570== 32,768 bytes in 1 blocks are possibly lost in loss record 2,084 of 2,096 ==00:00:44:39.764 3813570== at 0x4C30F0B: malloc (vg_replace_malloc.c:307) ==00:00:44:39.764 3813570== by 0x94315A: AllocSetAlloc (aset.c:941) ==00:00:44:39.764 3813570== by 0x94B52F: MemoryContextAlloc (mcxt.c:804) ==00:00:44:39.764 3813570== by 0x8023DA: LockAcquireExtended (lock.c:845) ==00:00:44:39.764 3813570== by 0x7FFC7E: LockRelationOid (lmgr.c:116) ==00:00:44:39.764 3813570== by 0x5CB89F: findDependentObjects (dependency.c:962) ==00:00:44:39.764 3813570== by 0x5CBA66: findDependentObjects (dependency.c:1060) ==00:00:44:39.764 3813570== by 0x5CBA66: findDependentObjects (dependency.c:1060) ==00:00:44:39.764 3813570== by 0x5CCB72: performMultipleDeletions (dependency.c:409) ==00:00:44:39.764 3813570== by 0x66F574: RemoveRelations (tablecmds.c:1395) ==00:00:44:39.764 3813570== by 0x81C986: ProcessUtilitySlow.isra.8 (utility.c:1698) ==00:00:44:39.764 3813570== by 0x81BCF2: standard_ProcessUtility (utility.c:1034) which evidently is warning that some LockMethodLocalHash entry is losing track of its lockOwners array. But I sure don't see how that could happen, nor why it'd only happen in this test. Could this be a false report? As you can see from the patch's additions to src/tools/valgrind.supp, I punted on the issues around pl/pgsql's function-compile-time leaks. We know that's an issue, but again the leaks are fairly small and they are confined within function cache entries, so I'm not sure how hard we should work on that. (An idea for silencing both this and the dictionary leak warnings is to install an on-proc-exit callback to drop the cached objects' contexts.) Anyway, looking through the patch, I found several non-cosmetic issues: * You were right to suspect that there was residual leakage in guc.c's handling of error cases. ProcessGUCArray and call_string_check_hook are both guilty of leaking malloc'd strings if an error in a proposed GUC setting is detected. * I still haven't tried to wrap my brain around the question of what RestoreGUCState really needs to be doing. I have, however, found that check-world passes just fine with the InitializeOneGUCOption calls diked out entirely, so that's what's in this patch. * libpqwalreceiver.c leaks a malloc'd error string when libpqrcv_check_conninfo detects an erroneous conninfo string. * spell.c leaks a compiled regular expression if an ispell dictionary cache entry is dropped. I fixed this by adding a memory context reset callback to release the regex. This is potentially a rather large leak, if the regex is complex (though typically it wouldn't be). * BuildEventTriggerCache leaks working storage into EventTriggerCacheContext. * Likewise, load_domaintype_info leaks working storage into a long-lived cache context. * RelationDestroyRelation leaks rd_statlist; the patch that added that failed to emulate the rd_indexlist prototype very well. * As previously noted, RelationInitTableAccessMethod causes leaks. * I made some effort to remove easily-removable leakage in lookup_ts_dictionary_cache itself, although given the leaks in its callees, this isn't terribly exciting. I am suspicious that there's something still not quite right in the memory context changes, because I noticed that the sanity_check.sql test (and no other ones) complained about row_description_context being leaked. I realized that the warning was because my compiler optimizes away the row_description_context static variable altogether, leaving no pointer to the context behind. I hacked around that in this patch by marking that variable volatile. However, that explanation just begs the question of why every run didn't show the same warning. I suppose that valgrind was considering the context to be referenced by some child or sibling context pointer, but if that's the explanation then we should never have seen the warning. I'm forced to the conclusion that valgrind is counting some but not all child/sibling context pointers, which sure seems like a bug. Maybe we need the two-level- mempool mechanism after all to get that to work better. Anyway, I think we need to commit and even back-patch the portion of the attached changes that are in * libpqwalreceiver.c * spell.h / spell.c * relcache.c * guc.c (except the quick hack in RestoreGUCState) Those are all genuine leaks that are in places where they could be repeated and thus potentially add up to something significant. There's a weaker case for applying the changes in evtcache.c, ts_cache.c, and typcache.c. Those are all basically leaving some cruft behind in a long-lived cache entry. But the cruft isn't huge and it would be recovered at cache flush, so I'm not convinced it amounts to a real-world problem. The rest of this is either working around valgrind shortcomings or jumping through a hoop to convince it that some data structure that's still around at exit is still referenced. Maybe we should commit some of it under "#ifdef USE_VALGRIND" tests. I really want to find some other answer than moving the dlist_node fields, though. Comments? regards, tom lane
-
Re: Getting better results from valgrind leak tracking
David Rowley <dgrowleyml@gmail.com> — 2021-03-28T22:48:47Z
On Wed, 17 Mar 2021 at 15:31, Andres Freund <andres@anarazel.de> wrote: > I'm a bit confused about the precise design of rs_private / > ParallelBlockTableScanWorkerData, specifically why it's been added to > TableScanDesc, instead of just adding it to HeapScanDesc? And why is it > allocated unconditionally, instead of just for parallel scans? That's a good point. In hindsight, I didn't spend enough effort questioning that design in the original patch. I see now that the rs_private field makes very little sense as we can just store what's private to heapam in HeapScanDescData. I've done that in the attached. I added the ParallelBlockTableScanWorkerData as a pointer field in HeapScanDescData and change it so we only allocate memory for it for just parallel scans. The field is left as NULL for non-parallel scans. I've also added a pfree in heap_endscan() to free the memory when the pointer is not NULL. I'm hoping that'll fix the valgrind warning, but I've not run it to check. David
-
Re: Getting better results from valgrind leak tracking
Andres Freund <andres@anarazel.de> — 2021-03-29T17:38:16Z
Hi, On 2021-03-29 11:48:47 +1300, David Rowley wrote: > I've done that in the attached. I added the > ParallelBlockTableScanWorkerData as a pointer field in > HeapScanDescData and change it so we only allocate memory for it for > just parallel scans. The field is left as NULL for non-parallel > scans. LGTM. > I've also added a pfree in heap_endscan() to free the memory when the > pointer is not NULL. I'm hoping that'll fix the valgrind warning, but > I've not run it to check. Cool. I think that's a good thing to do. The leak itself should already be fixed, and was more my fault... commit 415ffdc2205e209b6a73fb42a3fdd6e57e16c7b2 Author: Tom Lane <tgl@sss.pgh.pa.us> Date: 2021-03-18 20:50:56 -0400 Don't run RelationInitTableAccessMethod in a long-lived context. Greetings, Andres Freund -
Re: Getting better results from valgrind leak tracking
David Rowley <dgrowleyml@gmail.com> — 2021-03-29T21:18:11Z
On Tue, 30 Mar 2021 at 06:38, Andres Freund <andres@anarazel.de> wrote: > On 2021-03-29 11:48:47 +1300, David Rowley wrote: > > I've done that in the attached. I added the > > ParallelBlockTableScanWorkerData as a pointer field in > > HeapScanDescData and change it so we only allocate memory for it for > > just parallel scans. The field is left as NULL for non-parallel > > scans. > > LGTM. Thanks for having a look. Pushed. David