|
119 | 119 |
|
120 | 120 | #include <stdio.h> |
121 | 121 |
|
| 122 | +#ifdef GAP_MEM_CHECK |
| 123 | +#include <sys/mman.h> |
| 124 | +#endif |
| 125 | + |
122 | 126 | /**************************************************************************** |
123 | 127 | ** |
124 | 128 | *F WORDS_BAG( <size> ) . . . . . . . . . . words used by a bag of given size |
@@ -973,6 +977,119 @@ void InitCollectFuncBags ( |
973 | 977 | AfterCollectFuncBags = after_func; |
974 | 978 | } |
975 | 979 |
|
| 980 | +/*************************************************************** |
| 981 | + * GAP_MEM_CHECK |
| 982 | + * |
| 983 | + * One of the hardest categories of bugs to fix in GAP are where |
| 984 | + * a reference to the internals of a GAP object are kept across |
| 985 | + * a garbage collection (which moves GAP objects around). |
| 986 | + * |
| 987 | + * GAP_MEM_CHECK provides a method of detecting such problems, at |
| 988 | + * the cost of GREATLY decreased performance (Starting GAP in |
| 989 | + * --enableMemCheck mode takes days, rather than seconds). |
| 990 | + * |
| 991 | + * The fundamental idea behind GAP_MEM_CHECK is, whenever NewBag |
| 992 | + * or ResizeBag is called, then the contents of every Bag in |
| 993 | + * GAP is moved, and the memory previously being used is marked |
| 994 | + * as not readable or writable using 'mprotect'. |
| 995 | + * |
| 996 | + * Actually copying all GAP's memory space would be extremely |
| 997 | + * expensive, so instead we use 'mmap' to set up a set of copies |
| 998 | + * of the GAP memory space, which are represented by the same |
| 999 | + * underlying physical memory. |
| 1000 | + * |
| 1001 | + * The 0th such copy (which we also ensure is the one at the |
| 1002 | + * lowest memory address) is special -- this is where we |
| 1003 | + * reference the master pointers (which can't move). We do not |
| 1004 | + * 'mprotect' any of this memory. |
| 1005 | + * |
| 1006 | + * Every time we call 'NewBag' or 'ResizeBag', we change which |
| 1007 | + * copy of the GASMAN memory space the master pointers point |
| 1008 | + * to, disabling access to the previous copy, and enabling access |
| 1009 | + * to the new one. |
| 1010 | + * |
| 1011 | + * We never use the master pointers in any copy other than the |
| 1012 | + * 0th, and we never refer to the Bag area in the 0th copy. However, |
| 1013 | + * it simplifies things to not try to seperate the master pointer |
| 1014 | + * and Bag areas, because the master pointer area can grow as GAP |
| 1015 | + * runs. |
| 1016 | + * |
| 1017 | + * Because this code is VERY slow, it can be turned on and off. |
| 1018 | + * At run time, call GASMAN_MEM_CHECK(1) to enable, and |
| 1019 | + * GASMAN_MEM_CHECK(0) to disable. Start GAP with --enableMemCheck |
| 1020 | + * to enable from when GAP starts. |
| 1021 | + */ |
| 1022 | + |
| 1023 | +#ifdef GAP_MEM_CHECK |
| 1024 | + |
| 1025 | +Int EnableMemCheck = 0; |
| 1026 | + |
| 1027 | +Int enableMemCheck(Char ** argv, void * dummy) |
| 1028 | +{ |
| 1029 | + SyFputs( "# Warning: --enableMemCheck causes SEVERE slowdowns. Starting GAP may take several days!\n", 3 ); |
| 1030 | + EnableMemCheck = 1; |
| 1031 | + return 1; |
| 1032 | +} |
| 1033 | + |
| 1034 | +static void MoveBagMemory(char * oldbase, char * newbase) |
| 1035 | +{ |
| 1036 | + Int moveSize = (newbase - oldbase) / sizeof(Bag); |
| 1037 | + // update the masterpointers |
| 1038 | + for (Bag * p = MptrBags; p < MptrEndBags; p++) { |
| 1039 | + if ((Bag)MptrEndBags <= *p) |
| 1040 | + *p += moveSize; |
| 1041 | + } |
| 1042 | + |
| 1043 | + // update 'OldBags', 'YoungBags', 'AllocBags', and 'EndBags' |
| 1044 | + OldBags += moveSize; |
| 1045 | + YoungBags += moveSize; |
| 1046 | + AllocBags += moveSize; |
| 1047 | + EndBags += moveSize; |
| 1048 | +} |
| 1049 | + |
| 1050 | + |
| 1051 | +// Reference functions from system.c |
| 1052 | + |
| 1053 | +UInt GetMembufCount(void); |
| 1054 | +void * GetMembuf(UInt i); |
| 1055 | +UInt GetMembufSize(void); |
| 1056 | + |
| 1057 | +static void MaybeMoveBags(void) |
| 1058 | +{ |
| 1059 | + static Int oldBase = 0; |
| 1060 | + |
| 1061 | + if (!EnableMemCheck) |
| 1062 | + return; |
| 1063 | + |
| 1064 | + Int newBase = oldBase + 1; |
| 1065 | + // Memory buffer 0 is special, as we use that |
| 1066 | + // copy for the master pointers. Therefore never |
| 1067 | + // block access to it, and skip it when cycling. |
| 1068 | + if (newBase >= GetMembufCount()) |
| 1069 | + newBase = 1; |
| 1070 | + |
| 1071 | + // call the before function (if any) |
| 1072 | + if (BeforeCollectFuncBags != 0) |
| 1073 | + (*BeforeCollectFuncBags)(); |
| 1074 | + |
| 1075 | + MoveBagMemory(GetMembuf(oldBase), GetMembuf(newBase)); |
| 1076 | + |
| 1077 | + // Enable access to new memory |
| 1078 | + mprotect(GetMembuf(newBase), GetMembufSize(), PROT_READ | PROT_WRITE); |
| 1079 | + // Block access to old memory (except block 0, which will only occur |
| 1080 | + // on the first call). |
| 1081 | + if (oldBase != 0) { |
| 1082 | + mprotect(GetMembuf(oldBase), GetMembufSize(), PROT_NONE); |
| 1083 | + } |
| 1084 | + |
| 1085 | + // call the after function (if any) |
| 1086 | + if (AfterCollectFuncBags != 0) |
| 1087 | + (*AfterCollectFuncBags)(); |
| 1088 | + |
| 1089 | + |
| 1090 | + oldBase = newBase; |
| 1091 | +} |
| 1092 | +#endif |
976 | 1093 |
|
977 | 1094 | /**************************************************************************** |
978 | 1095 | ** |
@@ -1032,17 +1149,24 @@ void InitBags ( |
1032 | 1149 | SyAbortBags("cannot get storage for the initial workspace."); |
1033 | 1150 | EndBags = MptrBags + 1024*(initial_size / sizeof(Bag*)); |
1034 | 1151 |
|
| 1152 | + // In GAP_MEM_CHECK we want as few master pointers as possible, as we |
| 1153 | + // have to loop over them very frequently. |
| 1154 | +#ifdef GAP_MEM_CHECK |
| 1155 | + UInt initialBagCount = 100000; |
| 1156 | +#else |
| 1157 | + UInt initialBagCount = 1024*initial_size/8/sizeof(Bag*); |
| 1158 | +#endif |
1035 | 1159 | /* 1/8th of the storage goes into the masterpointer area */ |
1036 | 1160 | FreeMptrBags = (Bag)MptrBags; |
1037 | 1161 | for ( p = MptrBags; |
1038 | | - p + 2*(SIZE_MPTR_BAGS) <= MptrBags+1024*initial_size/8/sizeof(Bag*); |
| 1162 | + p + 2*(SIZE_MPTR_BAGS) <= MptrBags+initialBagCount; |
1039 | 1163 | p += SIZE_MPTR_BAGS ) |
1040 | 1164 | { |
1041 | 1165 | *p = (Bag)(p + SIZE_MPTR_BAGS); |
1042 | 1166 | } |
1043 | 1167 |
|
1044 | 1168 | /* the rest is for bags */ |
1045 | | - MptrEndBags = MptrBags + 1024*initial_size/8/sizeof(Bag*); |
| 1169 | + MptrEndBags = MptrBags + initialBagCount; |
1046 | 1170 | // Add a small gap between the end of the master pointers and OldBags |
1047 | 1171 | // This is mainly here to ensure we do not break allowing OldBags and |
1048 | 1172 | // MptrEndBags to differ. |
@@ -1104,6 +1228,10 @@ Bag NewBag ( |
1104 | 1228 | { |
1105 | 1229 | Bag bag; /* identifier of the new bag */ |
1106 | 1230 |
|
| 1231 | +#ifdef GAP_MEM_CHECK |
| 1232 | + MaybeMoveBags(); |
| 1233 | +#endif |
| 1234 | + |
1107 | 1235 | #ifdef TREMBLE_HEAP |
1108 | 1236 | CollectBags(0,0); |
1109 | 1237 | #endif |
@@ -1269,6 +1397,11 @@ UInt ResizeBag ( |
1269 | 1397 | Bag bag, |
1270 | 1398 | UInt new_size ) |
1271 | 1399 | { |
| 1400 | + |
| 1401 | +#ifdef GAP_MEM_CHECK |
| 1402 | + MaybeMoveBags(); |
| 1403 | +#endif |
| 1404 | + |
1272 | 1405 | #ifdef TREMBLE_HEAP |
1273 | 1406 | CollectBags(0,0); |
1274 | 1407 | #endif |
@@ -2064,12 +2197,15 @@ UInt CollectBags ( |
2064 | 2197 | && SyAllocBags(-512,0) ) |
2065 | 2198 | EndBags -= WORDS_BAG(512*1024L); |
2066 | 2199 |
|
| 2200 | +#if GAP_MEM_CHECK |
| 2201 | + UInt SpareMasterPointers = 100000; |
| 2202 | +#else |
| 2203 | + UInt SpareMasterPointers = SpaceBetweenPointers(EndBags, stopBags)/7; |
| 2204 | +#endif |
2067 | 2205 | /* if we want to increase the masterpointer area */ |
2068 | | - if ( SizeMptrsArea-NrLiveBags < SpaceBetweenPointers(EndBags,stopBags)/7 ) { |
2069 | | - |
| 2206 | + if ( SizeMptrsArea-NrLiveBags < SpareMasterPointers ) { |
2070 | 2207 | /* this is how many new masterpointers we want */ |
2071 | | - i = SpaceBetweenPointers(EndBags,stopBags)/7 - (SizeMptrsArea-NrLiveBags); |
2072 | | - |
| 2208 | + i = SpareMasterPointers - (SizeMptrsArea-NrLiveBags); |
2073 | 2209 | /* move the bags area */ |
2074 | 2210 | SyMemmove(OldBags+i, OldBags, SizeAllBagsArea*sizeof(*OldBags)); |
2075 | 2211 |
|
|
0 commit comments