Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Various memleak fixes. Drops the reachable-at-exit memory of (fossil rebuild) on this repo from 45MB to 680kb. Added fossil_atexit_free_this() to allow us to clean up function-local static allocations. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | memleak-fixes |
| Files: | files | file ages | folders |
| SHA3-256: |
00e6d7997c3e69a6a379fdd11b89a983 |
| User & Date: | stephan 2019-12-20 00:12:19.780 |
Context
|
2019-12-20
| ||
| 00:59 | atexit: no longer freeing about half of g.zXYZ because they're not always safe to free (sometimes they point to each other or are substrings of other strings). check-in: 5f36a86a73 user: stephan tags: memleak-fixes | |
| 00:12 | Various memleak fixes. Drops the reachable-at-exit memory of (fossil rebuild) on this repo from 45MB to 680kb. Added fossil_atexit_free_this() to allow us to clean up function-local static allocations. check-in: 00e6d7997c user: stephan tags: memleak-fixes | |
|
2019-12-19
| ||
| 20:41 | Clean up g.non-const zXYZ members in the atexit handler. Add a blob_reset() in finish_tag() to cover the case that fast_insert_content() does not reset it. check-in: 252ca35f47 user: stephan tags: memleak-fixes | |
Changes
Changes to src/bag.c.
| ︙ | ︙ | |||
46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
*/
struct Bag {
int cnt; /* Number of integers in the bag */
int sz; /* Number of slots in a[] */
int used; /* Number of used slots in a[] */
int *a; /* Hash table of integers that are in the bag */
};
#endif
/*
** Initialize a Bag structure
*/
void bag_init(Bag *p){
memset(p, 0, sizeof(*p));
| > > > > > | 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
*/
struct Bag {
int cnt; /* Number of integers in the bag */
int sz; /* Number of slots in a[] */
int used; /* Number of used slots in a[] */
int *a; /* Hash table of integers that are in the bag */
};
/*
** An expression for statically initializing a Bag instance, to be
** assigned to Bag instances at their declaration point.
*/
#define Bag_INIT {0,0,0,0}
#endif
/*
** Initialize a Bag structure
*/
void bag_init(Bag *p){
memset(p, 0, sizeof(*p));
|
| ︙ | ︙ |
Changes to src/content.c.
| ︙ | ︙ | |||
97 98 99 100 101 102 103 | contentCache.szTotal += blob_size(pBlob); p->content = *pBlob; blob_zero(pBlob); bag_insert(&contentCache.inCache, rid); } /* | | > > | > > > > > | 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
contentCache.szTotal += blob_size(pBlob);
p->content = *pBlob;
blob_zero(pBlob);
bag_insert(&contentCache.inCache, rid);
}
/*
** Clear the content cache. If it is passed true, it
** also frees all associated memory, otherwise it may
** retain parts for future uses of the cache.
*/
void content_clear_cache(int bFreeIt){
int i;
for(i=0; i<contentCache.n; i++){
blob_reset(&contentCache.a[i].content);
}
bag_clear(&contentCache.missing);
bag_clear(&contentCache.available);
bag_clear(&contentCache.inCache);
contentCache.n = 0;
contentCache.szTotal = 0;
if(bFreeIt){
fossil_free(contentCache.a);
contentCache.a = 0;
contentCache.nAlloc = 0;
}
}
/*
** Return the srcid associated with rid. Or return 0 if rid is
** original content and not a delta.
*/
int delta_source_rid(int rid){
|
| ︙ | ︙ |
Changes to src/db.c.
| ︙ | ︙ | |||
1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 |
const char *db_repository_filename(void){
static char *zRepo = 0;
assert( g.localOpen );
assert( g.zLocalRoot );
if( zRepo==0 ){
zRepo = db_lget("repository", 0);
if( zRepo && !file_is_absolute_path(zRepo) ){
zRepo = mprintf("%s%s", g.zLocalRoot, zRepo);
}
}
return zRepo;
}
/*
** Returns non-zero if the default value for the "allow-symlinks" setting
** is "on". When on Windows, this always returns false.
| > > > | 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 |
const char *db_repository_filename(void){
static char *zRepo = 0;
assert( g.localOpen );
assert( g.zLocalRoot );
if( zRepo==0 ){
zRepo = db_lget("repository", 0);
if( zRepo && !file_is_absolute_path(zRepo) ){
char * zFree = zRepo;
zRepo = mprintf("%s%s", g.zLocalRoot, zRepo);
fossil_free(zFree);
}
fossil_atexit_free_this(zRepo);
}
return zRepo;
}
/*
** Returns non-zero if the default value for the "allow-symlinks" setting
** is "on". When on Windows, this always returns false.
|
| ︙ | ︙ | |||
2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 |
db_swap_connections();
z = db_text(0, "SELECT value FROM global_config WHERE name=%Q", zName);
db_swap_connections();
}
if( pSetting!=0 && pSetting->versionable ){
/* This is a versionable setting, try and get the info from a
** checked out file */
z = db_get_versioned(zName, z);
}
if( z==0 ){
if( zDefault==0 && pSetting && pSetting->def[0] ){
z = fossil_strdup(pSetting->def);
}else{
z = fossil_strdup(zDefault);
}
| > > > > | 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 |
db_swap_connections();
z = db_text(0, "SELECT value FROM global_config WHERE name=%Q", zName);
db_swap_connections();
}
if( pSetting!=0 && pSetting->versionable ){
/* This is a versionable setting, try and get the info from a
** checked out file */
char * zZ = z;
z = db_get_versioned(zName, z);
if(zZ != z){
fossil_free(zZ);
}
}
if( z==0 ){
if( zDefault==0 && pSetting && pSetting->def[0] ){
z = fossil_strdup(pSetting->def);
}else{
z = fossil_strdup(zDefault);
}
|
| ︙ | ︙ |
Changes to src/main.c.
| ︙ | ︙ | |||
320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
** Macro for debugging:
*/
#define CGIDEBUG(X) if( g.fDebug ) cgi_debug X
#endif
Global g;
/*
** atexit() handler which frees up "some" of the resources
** used by fossil.
*/
static void fossil_atexit(void) {
static int once = 0;
| > > > > > > > > > > > > > > > > > > > > > > > > > > > | 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 |
** Macro for debugging:
*/
#define CGIDEBUG(X) if( g.fDebug ) cgi_debug X
#endif
Global g;
/*
** Infrastructure for fossil_atexit_free_this().
*/
static struct {
void* list[20]; /* Pointers to pass to fossil_free() during
** atexit(). */
int n; /* Number of items currently in this->list. */
} fossilFreeAtExit = { {0}, 0 };
/*
** If zMem is not NULL and there is space in fossil's atexit cleanup
** queue, zMem is added to that queue so that it will be passed to
** fossil_free() during the atexit() phase of app shutdown. If the
** queue is full or zMem is NULL, this function has no side effects.
**
** This is intended to be called by routines which allocate heap
** memory for static-scope values which otherwise won't be freed, and
** the static queue size is relatively small.
*/
void fossil_atexit_free_this(void * zMem){
if(zMem!=0
&& fossilFreeAtExit.n < (sizeof(fossilFreeAtExit.list)
/ sizeof(fossilFreeAtExit.list[0]))){
fossilFreeAtExit.list[fossilFreeAtExit.n++] = zMem;
}
}
/*
** atexit() handler which frees up "some" of the resources
** used by fossil.
*/
static void fossil_atexit(void) {
static int once = 0;
|
| ︙ | ︙ | |||
379 380 381 382 383 384 385 | fossil_free(g.zNonce); fossil_free(g.zOpenRevision); fossil_free(g.zPath); fossil_free(g.zRepositoryName); fossil_free(g.zRepositoryOption); fossil_free(g.zSshCmd); fossil_free(g.zTop); | > | > > | > > > > > > | 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 |
fossil_free(g.zNonce);
fossil_free(g.zOpenRevision);
fossil_free(g.zPath);
fossil_free(g.zRepositoryName);
fossil_free(g.zRepositoryOption);
fossil_free(g.zSshCmd);
fossil_free(g.zTop);
manifest_clear_cache();
content_clear_cache(1);
rebuild_clear_cache();
if(fossilFreeAtExit.n>0){
int i;
for(i = 0; i < fossilFreeAtExit.n; ++i){
fossil_free(fossilFreeAtExit.list[i]);
fossilFreeAtExit.list[i] = 0;
}
fossilFreeAtExit.n = 0;
}
/*
** FIXME: The next two lines cannot always be enabled; however, they
** are very useful for tracking down TH1 memory leaks.
*/
if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){
if( g.interp ){
Th_DeleteInterp(g.interp); g.interp = 0;
|
| ︙ | ︙ |
Changes to src/manifest.c.
| ︙ | ︙ | |||
66 67 68 69 70 71 72 |
/*
** A parsed manifest or cluster.
*/
struct Manifest {
Blob content; /* The original content blob */
int type; /* Type of artifact. One of CFTYPE_xxxxx */
int rid; /* The blob-id for this manifest */
| | | 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
/*
** A parsed manifest or cluster.
*/
struct Manifest {
Blob content; /* The original content blob */
int type; /* Type of artifact. One of CFTYPE_xxxxx */
int rid; /* The blob-id for this manifest */
const char *zBaseline;/* Baseline manifest. The B card. */
Manifest *pBaseline; /* The actual baseline manifest */
char *zComment; /* Decoded comment. The C card. */
double rDate; /* Date and time from D card. 0.0 if no D card. */
char *zUser; /* Name of the user from the U card. */
char *zRepoCksum; /* MD5 checksum of the baseline content. R card. */
char *zWiki; /* Text of the wiki page. W card. */
char *zWikiTitle; /* Name of the wiki page. L card. */
|
| ︙ | ︙ | |||
382 383 384 385 386 387 388 389 390 391 392 393 394 395 |
return c;
}
/*
** Shorthand for a control-artifact parsing error
*/
#define SYNTAX(T) {zErr=(T); goto manifest_syntax_error;}
/*
** Parse a blob into a Manifest object. The Manifest object
** takes over the input blob and will free it when the
** Manifest object is freed. Zeros are inserted into the blob
** as string terminators so that blob should not be used again.
**
| > > > > > > > > > > > > > | 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 |
return c;
}
/*
** Shorthand for a control-artifact parsing error
*/
#define SYNTAX(T) {zErr=(T); goto manifest_syntax_error;}
/*
** A cache of manifest IDs which manifest_parse() has seen in this
** session.
*/
static Bag seenManifests = Bag_INIT;
/*
** Frees all memory owned by the manifest "has-seen" cache. Intended
** to be called only from the app's atexit() handler.
*/
void manifest_clear_cache(){
bag_clear(&seenManifests);
}
/*
** Parse a blob into a Manifest object. The Manifest object
** takes over the input blob and will free it when the
** Manifest object is freed. Zeros are inserted into the blob
** as string terminators so that blob should not be used again.
**
|
| ︙ | ︙ | |||
426 427 428 429 430 431 432 | char *z; int n; char *zUuid; int sz = 0; int isRepeat; int nSelfTag = 0; /* Number of T cards referring to this manifest */ int nSimpleTag = 0; /* Number of T cards with "+" prefix */ | < | | | 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 |
char *z;
int n;
char *zUuid;
int sz = 0;
int isRepeat;
int nSelfTag = 0; /* Number of T cards referring to this manifest */
int nSimpleTag = 0; /* Number of T cards with "+" prefix */
const char *zErr = 0;
unsigned int m;
unsigned int seenCard = 0; /* Which card types have been seen */
char zErrBuf[100]; /* Write error messages here */
if( rid==0 ){
isRepeat = 1;
}else if( bag_find(&seenManifests, rid) ){
isRepeat = 1;
}else{
isRepeat = 0;
bag_insert(&seenManifests, rid);
}
/* Every structural artifact ends with a '\n' character. Exit early
** if that is not the case for this artifact.
*/
if( !isRepeat ) g.parseCnt[0]++;
z = blob_materialize(pContent);
|
| ︙ | ︙ | |||
1702 1703 1704 1705 1706 1707 1708 | ** ** Return the RID of the primary parent. */ static int manifest_add_checkin_linkages( int rid, /* The RID of the check-in */ Manifest *p, /* Manifest for this check-in */ int nParent, /* Number of parents for this check-in */ | | | 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 |
**
** Return the RID of the primary parent.
*/
static int manifest_add_checkin_linkages(
int rid, /* The RID of the check-in */
Manifest *p, /* Manifest for this check-in */
int nParent, /* Number of parents for this check-in */
char * const * azParent /* hashes for each parent */
){
int i;
int parentid = 0;
char zBaseId[30]; /* Baseline manifest RID for deltas. "NULL" otherwise */
Stmt q;
if( p->zBaseline ){
|
| ︙ | ︙ | |||
1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 |
blob_zero(&comment);
blob_zero(&brief);
if( once ){
once = 0;
zTitleExpr = db_get("ticket-title-expr", "title");
zStatusColumn = db_get("ticket-status-column", "status");
}
zTitle = db_text("unknown",
"SELECT \"%w\" FROM ticket WHERE tkt_uuid=%Q",
zTitleExpr, pManifest->zTicketUuid
);
if( !isNew ){
for(i=0; i<pManifest->nField; i++){
| > > | 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 |
blob_zero(&comment);
blob_zero(&brief);
if( once ){
once = 0;
zTitleExpr = db_get("ticket-title-expr", "title");
zStatusColumn = db_get("ticket-status-column", "status");
fossil_atexit_free_this(zTitleExpr);
fossil_atexit_free_this(zStatusColumn);
}
zTitle = db_text("unknown",
"SELECT \"%w\" FROM ticket WHERE tkt_uuid=%Q",
zTitleExpr, pManifest->zTicketUuid
);
if( !isNew ){
for(i=0; i<pManifest->nField; i++){
|
| ︙ | ︙ | |||
2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 |
if( tid ){
switch( p->aTag[i].zName[0] ){
case '-': type = 0; break; /* Cancel prior occurrences */
case '+': type = 1; break; /* Apply to target only */
case '*': type = 2; break; /* Propagate to descendants */
default:
fossil_error(1, "unknown tag type in manifest: %s", p->aTag);
return 0;
}
tag_insert(&p->aTag[i].zName[1], type, p->aTag[i].zValue,
rid, p->rDate, tid);
}
}
if( parentid ){
| > | 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 |
if( tid ){
switch( p->aTag[i].zName[0] ){
case '-': type = 0; break; /* Cancel prior occurrences */
case '+': type = 1; break; /* Apply to target only */
case '*': type = 2; break; /* Propagate to descendants */
default:
fossil_error(1, "unknown tag type in manifest: %s", p->aTag);
manifest_destroy(p);
return 0;
}
tag_insert(&p->aTag[i].zName[1], type, p->aTag[i].zValue,
rid, p->rDate, tid);
}
}
if( parentid ){
|
| ︙ | ︙ |
Changes to src/rebuild.c.
| ︙ | ︙ | |||
174 175 176 177 178 179 180 | ); } /* ** Variables used to store state information about an on-going "rebuild" ** or "deconstruct". */ | | | | | | > > > > > > > | 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
);
}
/*
** Variables used to store state information about an on-going "rebuild"
** or "deconstruct".
*/
static int totalSize; /* Total number of artifacts to process */
static int processCnt; /* Number processed so far */
static int ttyOutput; /* Do progress output */
static Bag bagDone = Bag_INIT; /* Bag of records rebuilt */
static char *zFNameFormat; /* Format string for filenames on deconstruct */
static int cchFNamePrefix; /* Length of directory prefix in zFNameFormat */
static const char *zDestDir;/* Destination directory on deconstruct */
static int prefixLength; /* Length of directory prefix for deconstruct */
static int fKeepRid1; /* Flag to preserve RID=1 on de- and reconstruct */
/*
** Draw the percent-complete message.
** The input is actually the permill complete.
*/
static void percent_complete(int permill){
static int lastOutput = -1;
if( permill>lastOutput ){
fossil_print(" %d.%d%% complete...\r", permill/10, permill%10);
fflush(stdout);
lastOutput = permill;
}
}
/*
** Frees rebuild-level cached state. Intended only to be called by the
** app-level atexit() handler.
*/
void rebuild_clear_cache(){
bag_clear(&bagDone);
}
/*
** Called after each artifact is processed
*/
static void rebuild_step_done(int rid){
/* assert( bag_find(&bagDone, rid)==0 ); */
bag_insert(&bagDone, rid);
|
| ︙ | ︙ | |||
364 365 366 367 368 369 370 |
*/
int rebuild_db(int randomize, int doOut, int doClustering){
Stmt s, q;
int errCnt = 0;
int incrSize;
Blob sql;
| | | 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 |
*/
int rebuild_db(int randomize, int doOut, int doClustering){
Stmt s, q;
int errCnt = 0;
int incrSize;
Blob sql;
bag_clear(&bagDone);
ttyOutput = doOut;
processCnt = 0;
if (ttyOutput && !g.fQuiet) {
percent_complete(0);
}
alert_triggers_disable();
rebuild_update_schema();
|
| ︙ | ︙ |
Changes to src/verify.c.
| ︙ | ︙ | |||
66 67 68 69 70 71 72 |
** This routine is called just prior to each commit operation.
**
** Invoke verify_rid() on every record that has been added or modified
** in the repository, in order to make sure that the repository is sane.
*/
static int verify_at_commit(void){
int rid;
| | | 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
** This routine is called just prior to each commit operation.
**
** Invoke verify_rid() on every record that has been added or modified
** in the repository, in order to make sure that the repository is sane.
*/
static int verify_at_commit(void){
int rid;
content_clear_cache(0);
inFinalVerify = 1;
rid = bag_first(&toVerify);
while( rid>0 ){
verify_rid(rid);
rid = bag_next(&toVerify, rid);
}
bag_clear(&toVerify);
|
| ︙ | ︙ |