From f552193183bf6f297ba811a0d348b86369ade098 Mon Sep 17 00:00:00 2001 From: Julian Seward Date: Sat, 22 Oct 2011 19:29:51 +0000 Subject: [PATCH] Allow garbage collection of the LAOG data structure(s). This avoids quadratic growth on some apparently simple test cases. Fixes #267925. (Philippe Waroquiers, philippe.waroquiers@skynet.be) git-svn-id: svn://svn.valgrind.org/valgrind/trunk@12201 --- helgrind/hg_main.c | 150 +++++++++++++++++++++++++- helgrind/hg_wordset.c | 99 ++++++++++++++++- helgrind/hg_wordset.h | 27 ++++- helgrind/tests/Makefile.am | 2 + helgrind/tests/t2t.c | 125 ++++++++++++++++++++++ helgrind/tests/t2t_laog.stderr.exp | 3 + helgrind/tests/t2t_laog.stdout.exp | 166 +++++++++++++++++++++++++++++ helgrind/tests/t2t_laog.vgtest | 4 + 8 files changed, 568 insertions(+), 8 deletions(-) create mode 100644 helgrind/tests/t2t.c create mode 100644 helgrind/tests/t2t_laog.stderr.exp create mode 100644 helgrind/tests/t2t_laog.stdout.exp create mode 100644 helgrind/tests/t2t_laog.vgtest diff --git a/helgrind/hg_main.c b/helgrind/hg_main.c index e89ef308ce..49e76925b1 100644 --- a/helgrind/hg_main.c +++ b/helgrind/hg_main.c @@ -136,6 +136,9 @@ static WordFM* map_locks = NULL; /* WordFM LockAddr Lock* */ /* The word-set universes for lock sets. */ static WordSetU* univ_lsets = NULL; /* sets of Lock* */ static WordSetU* univ_laog = NULL; /* sets of Lock*, for LAOG */ +static Int next_gc_univ_laog = 1; +/* univ_laog will be garbaged collected when the nr of element in univ_laog is + >= next_gc_univ_laog. */ /* Allow libhb to get at the universe of locksets stored here. Sigh. */ @@ -3300,6 +3303,85 @@ static void laog__show ( Char* who ) { VG_(printf)("}\n"); } +static void univ_laog_do_GC ( void ) { + Word i; + LAOGLinks* links; + Word seen = 0; + Int prev_next_gc_univ_laog = next_gc_univ_laog; + const UWord univ_laog_cardinality = HG_(cardinalityWSU)( univ_laog); + + Bool *univ_laog_seen = HG_(zalloc) ( "hg.gc_univ_laog.1", + (Int) univ_laog_cardinality + * sizeof(Bool) ); + // univ_laog_seen[*] set to 0 (False) by zalloc. + + if (VG_(clo_stats)) + VG_(message)(Vg_DebugMsg, + "univ_laog_do_GC enter cardinality %'10d\n", + (Int)univ_laog_cardinality); + + VG_(initIterFM)( laog ); + links = NULL; + while (VG_(nextIterFM)( laog, NULL, (UWord*)&links )) { + tl_assert(links); + tl_assert(links->inns >= 0 && links->inns < univ_laog_cardinality); + univ_laog_seen[links->inns] = True; + tl_assert(links->outs >= 0 && links->outs < univ_laog_cardinality); + univ_laog_seen[links->outs] = True; + links = NULL; + } + VG_(doneIterFM)( laog ); + + for (i = 0; i < (Int)univ_laog_cardinality; i++) { + if (univ_laog_seen[i]) + seen++; + else + HG_(dieWS) ( univ_laog, (WordSet)i ); + } + + HG_(free) (univ_laog_seen); + + // We need to decide the value of the next_gc. + // 3 solutions were looked at: + // Sol 1: garbage collect at seen * 2 + // This solution was a lot slower, probably because we both do a lot of + // garbage collection and do not keep long enough laog WV that will become + // useful again very soon. + // Sol 2: garbage collect at a percentage increase of the current cardinality + // (with a min increase of 1) + // Trials on a small test program with 1%, 5% and 10% increase was done. + // 1% is slightly faster than 5%, which is slightly slower than 10%. + // However, on a big application, this caused the memory to be exhausted, + // as even a 1% increase of size at each gc becomes a lot, when many gc + // are done. + // Sol 3: always garbage collect at current cardinality + 1. + // This solution was the fastest of the 3 solutions, and caused no memory + // exhaustion in the big application. + // + // With regards to cost introduced by gc: on the t2t perf test (doing only + // lock/unlock operations), t2t 50 10 2 was about 25% faster than the + // version with garbage collection. With t2t 50 20 2, my machine started + // to page out, and so the garbage collected version was much faster. + // On smaller lock sets (e.g. t2t 20 5 2, giving about 100 locks), the + // difference performance is insignificant (~ 0.1 s). + // Of course, it might be that real life programs are not well represented + // by t2t. + + // If ever we want to have a more sophisticated control + // (e.g. clo options to control the percentage increase or fixed increased), + // we should do it here, eg. + // next_gc_univ_laog = prev_next_gc_univ_laog + VG_(clo_laog_gc_fixed); + // Currently, we just hard-code the solution 3 above. + next_gc_univ_laog = prev_next_gc_univ_laog + 1; + + if (VG_(clo_stats)) + VG_(message) + (Vg_DebugMsg, + "univ_laog_do_GC exit seen %'8d next gc at cardinality %'10d\n", + (Int)seen, next_gc_univ_laog); +} + + __attribute__((noinline)) static void laog__add_edge ( Lock* src, Lock* dst ) { Word keyW; @@ -3378,13 +3460,16 @@ static void laog__add_edge ( Lock* src, Lock* dst ) { VG_(addToFM)( laog_exposition, (Word)expo2, (Word)NULL ); } } + + if (HG_(cardinalityWSU) (univ_laog) >= next_gc_univ_laog) + univ_laog_do_GC(); } __attribute__((noinline)) static void laog__del_edge ( Lock* src, Lock* dst ) { Word keyW; LAOGLinks* links; - if (0) VG_(printf)("laog__del_edge %p %p\n", src, dst); + if (0) VG_(printf)("laog__del_edge enter %p %p\n", src, dst); /* Update the out edges for src */ keyW = 0; links = NULL; @@ -3401,6 +3486,27 @@ static void laog__del_edge ( Lock* src, Lock* dst ) { tl_assert(keyW == (Word)dst); links->inns = HG_(delFromWS)( univ_laog, links->inns, (Word)src ); } + + /* Remove the exposition of src,dst (if present) */ + { + LAOGLinkExposition *fm_expo; + + LAOGLinkExposition expo; + expo.src_ga = src->guestaddr; + expo.dst_ga = dst->guestaddr; + expo.src_ec = NULL; + expo.dst_ec = NULL; + + if (VG_(delFromFM) (laog_exposition, + (UWord*)&fm_expo, NULL, (UWord)&expo )) { + HG_(free) (fm_expo); + } + } + + /* deleting edges can increase nr of of WS so check for gc. */ + if (HG_(cardinalityWSU) (univ_laog) >= next_gc_univ_laog) + univ_laog_do_GC(); + if (0) VG_(printf)("laog__del_edge exit\n"); } __attribute__((noinline)) @@ -3611,6 +3717,21 @@ static void laog__pre_thread_acquires_lock ( all_except_Locks__sanity_check("laog__pre_thread_acquires_lock-post"); } +/* Allocates a duplicate of words. Caller must HG_(free) the result. */ +static UWord* UWordV_dup(UWord* words, Word words_size) +{ + UInt i; + + if (words_size == 0) + return NULL; + + UWord *dup = HG_(zalloc) ("hg.dup.1", (SizeT) words_size * sizeof(UWord)); + + for (i = 0; i < words_size; i++) + dup[i] = words[i]; + + return dup; +} /* Delete from 'laog' any pair mentioning a lock in locksToDelete */ @@ -3624,11 +3745,17 @@ static void laog__handle_one_lock_deletion ( Lock* lk ) preds = laog__preds( lk ); succs = laog__succs( lk ); + // We need to duplicate the payload, as these can be garbage collected + // during the del/add operations below. HG_(getPayloadWS)( &preds_words, &preds_size, univ_laog, preds ); + preds_words = UWordV_dup(preds_words, preds_size); + + HG_(getPayloadWS)( &succs_words, &succs_size, univ_laog, succs ); + succs_words = UWordV_dup(succs_words, succs_size); + for (i = 0; i < preds_size; i++) laog__del_edge( (Lock*)preds_words[i], lk ); - HG_(getPayloadWS)( &succs_words, &succs_size, univ_laog, succs ); for (j = 0; j < succs_size; j++) laog__del_edge( lk, (Lock*)succs_words[j] ); @@ -3642,6 +3769,24 @@ static void laog__handle_one_lock_deletion ( Lock* lk ) } } } + + if (preds_words) + HG_(free) (preds_words); + if (succs_words) + HG_(free) (succs_words); + + // Remove lk information from laog links FM + { + LAOGLinks *links; + Lock* linked_lk; + + if (VG_(delFromFM) (laog, + (UWord*)&linked_lk, (UWord*)&links, (UWord)lk)) { + tl_assert (linked_lk == lk); + HG_(free) (links); + } + } + /* FIXME ??? What about removing lock lk data from EXPOSITION ??? */ } //__attribute__((noinline)) @@ -3654,6 +3799,7 @@ static void laog__handle_one_lock_deletion ( Lock* lk ) // // // HG_(getPayloadWS)( &ws_words, &ws_size, univ_lsets, locksToDelete ); +// UWordV_dup call needed here ... // for (i = 0; i < ws_size; i++) // laog__handle_one_lock_deletion( (Lock*)ws_words[i] ); // diff --git a/helgrind/hg_wordset.c b/helgrind/hg_wordset.c index a7cb814774..468ff168e0 100644 --- a/helgrind/hg_wordset.c +++ b/helgrind/hg_wordset.c @@ -44,6 +44,9 @@ #include "hg_basics.h" #include "hg_wordset.h" /* self */ +// define to 1 to have (a lot of) debugging of add/re-use/die WSU entries. +#define HG_DEBUG 0 + //------------------------------------------------------------------// //--- Word Cache ---// //------------------------------------------------------------------// @@ -139,7 +142,11 @@ typedef /* ix2vec[0 .. ix2vec_used-1] are pointers to the lock sets (WordVecs) really. vec2ix is the inverse mapping, mapping WordVec* to the corresponding ix2vec entry number. The two mappings are mutually - redundant. */ + redundant. + + If a WordVec WV is marked as dead by HG(dieWS), WV is removed from + vec2ix. The entry of the dead WVs in ix2vec are used to maintain a + linked list of free (to be re-used) ix2vec entries. */ struct _WordSetU { void* (*alloc)(HChar*,SizeT); HChar* cc; @@ -148,6 +155,7 @@ struct _WordSetU { WordVec** ix2vec; /* WordSet-to-WordVec mapping array */ UWord ix2vec_size; UWord ix2vec_used; + WordVec** ix2vec_free; WordSet empty; /* cached, for speed */ /* Caches for some operations */ WCache cache_addTo; @@ -159,6 +167,7 @@ struct _WordSetU { UWord n_add_uncached; UWord n_del; UWord n_del_uncached; + UWord n_die; UWord n_union; UWord n_intersect; UWord n_intersect_uncached; @@ -235,7 +244,7 @@ static void ensure_ix2vec_space ( WordSetU* wsu ) if (wsu->ix2vec_used < wsu->ix2vec_size) return; new_sz = 2 * wsu->ix2vec_size; - if (new_sz == 0) new_sz = 2; + if (new_sz == 0) new_sz = 1; new_vec = wsu->alloc( wsu->cc, new_sz * sizeof(WordVec*) ); tl_assert(new_vec); for (i = 0; i < wsu->ix2vec_size; i++) @@ -246,9 +255,20 @@ static void ensure_ix2vec_space ( WordSetU* wsu ) wsu->ix2vec_size = new_sz; } +/* True if wv is a dead entry (i.e. is in the linked list of free to be re-used + entries in ix2vec). */ +static inline Bool is_dead ( WordSetU* wsu, WordVec* wv ) +{ + if (wv == NULL) /* last element in free linked list in ix2vec */ + return True; + else + return (WordVec**)wv >= &(wsu->ix2vec[1]) + && (WordVec**)wv < &(wsu->ix2vec[wsu->ix2vec_size]); +} /* Index into a WordSetU, doing the obvious range check. Failure of the assertions marked XXX and YYY is an indication of passing the - wrong WordSetU* in the public API of this module. */ + wrong WordSetU* in the public API of this module. + Accessing a dead ws will assert. */ static WordVec* do_ix2vec ( WordSetU* wsu, WordSet ws ) { WordVec* wv; @@ -259,12 +279,32 @@ static WordVec* do_ix2vec ( WordSetU* wsu, WordSet ws ) that does not come from the 'wsu' universe. */ tl_assert(ws < wsu->ix2vec_used); /* XXX */ wv = wsu->ix2vec[ws]; - /* Make absolutely sure that 'ws' is a member of 'wsu'. */ + /* Make absolutely sure that 'ws' is a non dead member of 'wsu'. */ tl_assert(wv); + tl_assert(!is_dead(wsu,wv)); tl_assert(wv->owner == wsu); /* YYY */ return wv; } +/* Same as do_ix2vec but returns NULL for a dead ws. */ +static WordVec* do_ix2vec_with_dead ( WordSetU* wsu, WordSet ws ) +{ + WordVec* wv; + tl_assert(wsu->ix2vec_used <= wsu->ix2vec_size); + if (wsu->ix2vec_used > 0) + tl_assert(wsu->ix2vec); + /* If this assertion fails, it may mean you supplied a 'ws' + that does not come from the 'wsu' universe. */ + tl_assert(ws < wsu->ix2vec_used); /* XXX */ + wv = wsu->ix2vec[ws]; + /* Make absolutely sure that 'ws' is either dead or a member of 'wsu'. */ + if (is_dead(wsu,wv)) + wv = NULL; + else + tl_assert(wv->owner == wsu); /* YYY */ + return wv; +} + /* See if wv is contained within wsu. If so, deallocate wv and return the index of the already-present copy. If not, add wv to both the vec2ix and ix2vec mappings and return its index. @@ -289,13 +329,23 @@ static WordSet add_or_dealloc_WordVec( WordSetU* wsu, WordVec* wv_new ) tl_assert(wsu->ix2vec[ix_old] == wv_old); delete_WV( wv_new ); return (WordSet)ix_old; + } else if (wsu->ix2vec_free) { + WordSet ws; + tl_assert(is_dead(wsu,(WordVec*)wsu->ix2vec_free)); + ws = wsu->ix2vec_free - &(wsu->ix2vec[0]); + tl_assert(wsu->ix2vec[ws] == NULL || is_dead(wsu,wsu->ix2vec[ws])); + wsu->ix2vec_free = (WordVec **) wsu->ix2vec[ws]; + wsu->ix2vec[ws] = wv_new; + VG_(addToFM)( wsu->vec2ix, (Word)wv_new, ws ); + if (HG_DEBUG) VG_(printf)("aodW %s re-use free %d %p\n", wsu->cc, (Int)ws, wv_new ); + return ws; } else { ensure_ix2vec_space( wsu ); tl_assert(wsu->ix2vec); tl_assert(wsu->ix2vec_used < wsu->ix2vec_size); wsu->ix2vec[wsu->ix2vec_used] = wv_new; VG_(addToFM)( wsu->vec2ix, (Word)wv_new, (Word)wsu->ix2vec_used ); - if (0) VG_(printf)("aodW %d\n", (Int)wsu->ix2vec_used ); + if (HG_DEBUG) VG_(printf)("aodW %s %d %p\n", wsu->cc, (Int)wsu->ix2vec_used, wv_new ); wsu->ix2vec_used++; tl_assert(wsu->ix2vec_used <= wsu->ix2vec_size); return (WordSet)(wsu->ix2vec_used - 1); @@ -321,6 +371,7 @@ WordSetU* HG_(newWordSetU) ( void* (*alloc_nofail)( HChar*, SizeT ), wsu->ix2vec_used = 0; wsu->ix2vec_size = 0; wsu->ix2vec = NULL; + wsu->ix2vec_free = NULL; WCache_INIT(wsu->cache_addTo, cacheSize); WCache_INIT(wsu->cache_delFrom, cacheSize); WCache_INIT(wsu->cache_intersect, cacheSize); @@ -397,6 +448,7 @@ void HG_(getPayloadWS) ( /*OUT*/UWord** words, /*OUT*/UWord* nWords, WordSetU* wsu, WordSet ws ) { WordVec* wv; + if (HG_DEBUG) VG_(printf)("getPayloadWS %s %d\n", wsu->cc, (Int)ws); tl_assert(wsu); wv = do_ix2vec( wsu, ws ); tl_assert(wv->size >= 0); @@ -404,6 +456,42 @@ void HG_(getPayloadWS) ( /*OUT*/UWord** words, /*OUT*/UWord* nWords, *words = wv->words; } +void HG_(dieWS) ( WordSetU* wsu, WordSet ws ) +{ + WordVec* wv = do_ix2vec_with_dead( wsu, ws ); + WordVec* wv_in_vec2ix; + UWord/*Set*/ wv_ix = -1; + + if (HG_DEBUG) VG_(printf)("dieWS %s %d %p\n", wsu->cc, (Int)ws, wv); + + if (ws == 0) + return; // we never die the empty set. + + if (!wv) + return; // already dead. (or a bug ?). + + wsu->n_die++; + + + wsu->ix2vec[ws] = (WordVec*) wsu->ix2vec_free; + wsu->ix2vec_free = &wsu->ix2vec[ws]; + + VG_(delFromFM) ( wsu->vec2ix, + (Word*)&wv_in_vec2ix, (Word*)&wv_ix, + (Word)wv ); + + if (HG_DEBUG) VG_(printf)("dieWS wv_ix %d\n", (Int)wv_ix); + tl_assert (wv_ix); + tl_assert (wv_ix == ws); + + delete_WV( wv ); + + wsu->cache_addTo.inUse = 0; + wsu->cache_delFrom.inUse = 0; + wsu->cache_intersect.inUse = 0; + wsu->cache_minus.inUse = 0; +} + Bool HG_(plausibleWS) ( WordSetU* wsu, WordSet ws ) { if (wsu == NULL) return False; @@ -511,6 +599,7 @@ void HG_(ppWSUstats) ( WordSetU* wsu, HChar* name ) VG_(printf)(" isSingleton %10lu\n", wsu->n_isSingleton); VG_(printf)(" anyElementOf %10lu\n", wsu->n_anyElementOf); VG_(printf)(" isSubsetOf %10lu\n", wsu->n_isSubsetOf); + VG_(printf)(" dieWS %10lu\n", wsu->n_die); } WordSet HG_(addToWS) ( WordSetU* wsu, WordSet ws, UWord w ) diff --git a/helgrind/hg_wordset.h b/helgrind/hg_wordset.h index 54a175a12f..20f8bdb3a3 100644 --- a/helgrind/hg_wordset.h +++ b/helgrind/hg_wordset.h @@ -55,7 +55,8 @@ WordSetU* HG_(newWordSetU) ( void* (*alloc_nofail)( HChar*, SizeT ), /* Free up the WordSetU. */ void HG_(deleteWordSetU) ( WordSetU* ); -/* Get the number of elements in this WordSetU. */ +/* Get the number of elements in this WordSetU. Note that the dead + WordSet are included in the WordSetU number of elements. */ UWord HG_(cardinalityWSU) ( WordSetU* ); /* Show performance stats for this WordSetU. */ @@ -82,12 +83,36 @@ WordSet HG_(singletonWS) ( WordSetU*, UWord ); WordSet HG_(isSubsetOf) ( WordSetU*, WordSet, WordSet ); Bool HG_(plausibleWS) ( WordSetU*, WordSet ); + + Bool HG_(saneWS_SLOW) ( WordSetU*, WordSet ); void HG_(ppWS) ( WordSetU*, WordSet ); + void HG_(getPayloadWS) ( /*OUT*/UWord** words, /*OUT*/UWord* nWords, WordSetU*, WordSet ); +/* HG_(dieWS) indicates WordSet is not used/not referenced anymore, + and its memory can be reclaimed. + If ever a WordSet with the same content would be needed again, + a new WordSet will be reallocated. + + BUG ALERT: !!! Using HG_(dieWS) on a WSU introduces a risk of + dangling references. Dangling references can be created by keeping + a ws after having marked it dead. This ws (just an index in + reality) will be re-cycled : a newly created wv can get the same + index. This implies that the wrong wv will be used if the + "old" ws has been kept. + Re-using a "dead" ws will be detected if the index has not been + re-cycled yet. + + Another possibility of bug is to ask for the payload of a ws, and + then have this ws marked dead while the payload is still being + examined. This is a real dangling reference in free or re-allocated + memory. */ +void HG_(dieWS) ( WordSetU*, WordSet ); + + //------------------------------------------------------------------// //--- end WordSet ---// diff --git a/helgrind/tests/Makefile.am b/helgrind/tests/Makefile.am index 996a8e6905..3271a54275 100644 --- a/helgrind/tests/Makefile.am +++ b/helgrind/tests/Makefile.am @@ -43,6 +43,7 @@ EXTRA_DIST = \ pth_spinlock.vgtest pth_spinlock.stdout.exp pth_spinlock.stderr.exp \ rwlock_race.vgtest rwlock_race.stdout.exp rwlock_race.stderr.exp \ rwlock_test.vgtest rwlock_test.stdout.exp rwlock_test.stderr.exp \ + t2t_laog.vgtest t2t_laog.stdout.exp t2t_laog.stderr.exp \ tc01_simple_race.vgtest tc01_simple_race.stdout.exp \ tc01_simple_race.stderr.exp \ tc02_simple_tls.vgtest tc02_simple_tls.stdout.exp \ @@ -105,6 +106,7 @@ check_PROGRAMS = \ locked_vs_unlocked1 \ locked_vs_unlocked2 \ locked_vs_unlocked3 \ + t2t \ tc01_simple_race \ tc02_simple_tls \ tc03_re_excl \ diff --git a/helgrind/tests/t2t.c b/helgrind/tests/t2t.c new file mode 100644 index 0000000000..d8a0abae15 --- /dev/null +++ b/helgrind/tests/t2t.c @@ -0,0 +1,125 @@ +#include +#include +#include +#define MANY 1000 +#define LEVEL 100 +typedef struct { + pthread_mutex_t m[MANY]; + pthread_mutex_t d; +} Level; + +static Level level[LEVEL]; + +static int stat_mutex_init = 0; +static int stat_mutex_lock = 0; +static int stat_mutex_unlock = 0; +static int stat_mutex_destroy = 0; + +/* t2t.c : test program for the laog data structure performance testing + and "shaking" : it creates, locks/unlocks and destroy mutex. + + USAGE: t2t [many] [level] [loops] + many (default 100) : how many locks are created/locked/unlocked at a certain level. + level (default 1) : how many levels of "nested locks" are done + loops : how many times these locks are created and destroyed and locked/unlocked) */ +#define check if (ret != 0) printf("error %d at line %d\n", ret, __LINE__) +int doit(int argc, char*argv[]) +{ + int l, i; + int ret; + + int clo_many = 100; + int clo_level = 1; + + if (argc >= 2) clo_many = atoi(argv[1]); + if (argc >= 3) clo_level = atoi(argv[2]); + + if (clo_many > MANY) { + printf("error argv[1] (many arg) %d > max MANY %d\n", clo_many, MANY); + exit(1); + } + + if (clo_level > LEVEL) { + printf("error argv[2] (level arg) %d > max LEVEL %d\n", clo_level, LEVEL); + exit(1); + } + + printf ("many %d level %d total_locks: %d\n", + clo_many, clo_level, + clo_many * clo_level + clo_level * (clo_level == 1 ? 0 : 1)); + + for (l = 0; l < clo_level; l++) { + printf ("init level %d\n", l); + for (i = 0; i < clo_many; i++) { + ret = pthread_mutex_init (&level[l].m[i], NULL); + check; + stat_mutex_init++; + } + if (clo_level > 1) { + ret = pthread_mutex_init (&level[l].d, NULL); + check; + stat_mutex_init++; + } + } + + for (l = 0; l < clo_level; l++) { + printf ("locking level %d\n", l); + for (i = 0; i < clo_many; i++) { + ret = pthread_mutex_lock (&level[l].m[i]); + check; + stat_mutex_lock++; + } + if (clo_level > 1) { + ret = pthread_mutex_lock (&level[l].d); + check; + stat_mutex_lock++; + } + } + + for (l = 0; l < clo_level; l++) { + printf ("unlocking level %d\n", l); + for (i = 0; i < clo_many; i++) { + ret = pthread_mutex_unlock (&level[l].m[i]); + check; + stat_mutex_unlock++; + } + if (clo_level > 1) { + ret = pthread_mutex_unlock (&level[l].d); + stat_mutex_unlock++; + check; + } + } + + for (l = 0; l < clo_level; l++) { + printf ("deleting level %d\n", l); + if (clo_level > 1) { + ret = pthread_mutex_destroy (&level[l].d); + /// this tests the influence of the deletion in another order. + check; + stat_mutex_destroy++; + } + for (i = 0; i < clo_many; i++) { + ret = pthread_mutex_destroy (&level[l].m[i]); + check; + stat_mutex_destroy++; + } + } + return 0; +} + +int main(int argc, char*argv[]) +{ + int loops = 1; + int i; + if (argc >= 4) loops = atoi(argv[3]); + + printf ("loops %d\n", loops); + for (i = 0; i < loops; i++) + doit(argc, argv); + + printf ("stats: init %d lock %d unlock %d destroy %d\n", + stat_mutex_init, stat_mutex_lock, + stat_mutex_unlock, stat_mutex_destroy); + return 0; +} + diff --git a/helgrind/tests/t2t_laog.stderr.exp b/helgrind/tests/t2t_laog.stderr.exp new file mode 100644 index 0000000000..d18786f806 --- /dev/null +++ b/helgrind/tests/t2t_laog.stderr.exp @@ -0,0 +1,3 @@ + + +ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff --git a/helgrind/tests/t2t_laog.stdout.exp b/helgrind/tests/t2t_laog.stdout.exp new file mode 100644 index 0000000000..364d87628f --- /dev/null +++ b/helgrind/tests/t2t_laog.stdout.exp @@ -0,0 +1,166 @@ +loops 4 +many 30 level 10 total_locks: 310 +init level 0 +init level 1 +init level 2 +init level 3 +init level 4 +init level 5 +init level 6 +init level 7 +init level 8 +init level 9 +locking level 0 +locking level 1 +locking level 2 +locking level 3 +locking level 4 +locking level 5 +locking level 6 +locking level 7 +locking level 8 +locking level 9 +unlocking level 0 +unlocking level 1 +unlocking level 2 +unlocking level 3 +unlocking level 4 +unlocking level 5 +unlocking level 6 +unlocking level 7 +unlocking level 8 +unlocking level 9 +deleting level 0 +deleting level 1 +deleting level 2 +deleting level 3 +deleting level 4 +deleting level 5 +deleting level 6 +deleting level 7 +deleting level 8 +deleting level 9 +many 30 level 10 total_locks: 310 +init level 0 +init level 1 +init level 2 +init level 3 +init level 4 +init level 5 +init level 6 +init level 7 +init level 8 +init level 9 +locking level 0 +locking level 1 +locking level 2 +locking level 3 +locking level 4 +locking level 5 +locking level 6 +locking level 7 +locking level 8 +locking level 9 +unlocking level 0 +unlocking level 1 +unlocking level 2 +unlocking level 3 +unlocking level 4 +unlocking level 5 +unlocking level 6 +unlocking level 7 +unlocking level 8 +unlocking level 9 +deleting level 0 +deleting level 1 +deleting level 2 +deleting level 3 +deleting level 4 +deleting level 5 +deleting level 6 +deleting level 7 +deleting level 8 +deleting level 9 +many 30 level 10 total_locks: 310 +init level 0 +init level 1 +init level 2 +init level 3 +init level 4 +init level 5 +init level 6 +init level 7 +init level 8 +init level 9 +locking level 0 +locking level 1 +locking level 2 +locking level 3 +locking level 4 +locking level 5 +locking level 6 +locking level 7 +locking level 8 +locking level 9 +unlocking level 0 +unlocking level 1 +unlocking level 2 +unlocking level 3 +unlocking level 4 +unlocking level 5 +unlocking level 6 +unlocking level 7 +unlocking level 8 +unlocking level 9 +deleting level 0 +deleting level 1 +deleting level 2 +deleting level 3 +deleting level 4 +deleting level 5 +deleting level 6 +deleting level 7 +deleting level 8 +deleting level 9 +many 30 level 10 total_locks: 310 +init level 0 +init level 1 +init level 2 +init level 3 +init level 4 +init level 5 +init level 6 +init level 7 +init level 8 +init level 9 +locking level 0 +locking level 1 +locking level 2 +locking level 3 +locking level 4 +locking level 5 +locking level 6 +locking level 7 +locking level 8 +locking level 9 +unlocking level 0 +unlocking level 1 +unlocking level 2 +unlocking level 3 +unlocking level 4 +unlocking level 5 +unlocking level 6 +unlocking level 7 +unlocking level 8 +unlocking level 9 +deleting level 0 +deleting level 1 +deleting level 2 +deleting level 3 +deleting level 4 +deleting level 5 +deleting level 6 +deleting level 7 +deleting level 8 +deleting level 9 +stats: init 1240 lock 1240 unlock 1240 destroy 1240 diff --git a/helgrind/tests/t2t_laog.vgtest b/helgrind/tests/t2t_laog.vgtest new file mode 100644 index 0000000000..ce27938343 --- /dev/null +++ b/helgrind/tests/t2t_laog.vgtest @@ -0,0 +1,4 @@ +# performance test for a big laog graph + shake the add/del +# in the laog data structure. +prog: t2t +args: 30 10 4 -- 2.47.2