diff --git a/Makefile b/Makefile index 044fd54e9de225..f4e93eb30d7966 100644 --- a/Makefile +++ b/Makefile @@ -830,6 +830,7 @@ LIB_OBJS += lockfile.o LIB_OBJS += log-tree.o LIB_OBJS += mailinfo.o LIB_OBJS += mailmap.o +LIB_OBJS += mem-pool.o LIB_OBJS += match-trees.o LIB_OBJS += merge.o LIB_OBJS += merge-blobs.o diff --git a/apply.c b/apply.c index 476a4f48566222..2a0a6caf42645f 100644 --- a/apply.c +++ b/apply.c @@ -4108,12 +4108,12 @@ static int build_fake_ancestor(struct apply_state *state, struct patch *list) return error(_("sha1 information is lacking or useless " "(%s)."), name); - ce = make_cache_entry(patch->old_mode, oid.hash, name, 0, 0); + ce = make_cache_entry_from_index(&result, patch->old_mode, oid.hash, name, 0, 0); if (!ce) - return error(_("make_cache_entry failed for path '%s'"), + return error(_("make_cache_entry_from_index failed for path '%s'"), name); if (add_index_entry(&result, ce, ADD_CACHE_OK_TO_ADD)) { - free(ce); + cache_entry_free(ce); return error(_("could not add %s to temporary index"), name); } @@ -4281,12 +4281,11 @@ static int add_index_file(struct apply_state *state, struct stat st; struct cache_entry *ce; int namelen = strlen(path); - unsigned ce_size = cache_entry_size(namelen); if (!state->update_index) return 0; - ce = xcalloc(1, ce_size); + ce = make_empty_cache_entry(namelen); memcpy(ce->name, path, namelen); ce->ce_mode = create_ce_mode(mode); ce->ce_flags = create_ce_flags(0); @@ -4296,13 +4295,13 @@ static int add_index_file(struct apply_state *state, if (!skip_prefix(buf, "Subproject commit ", &s) || get_oid_hex(s, &ce->oid)) { - free(ce); - return error(_("corrupt patch for submodule %s"), path); + cache_entry_free(ce); + return error(_("corrupt patch for submodule %s"), path); } } else { if (!state->cached) { if (lstat(path, &st) < 0) { - free(ce); + cache_entry_free(ce); return error_errno(_("unable to stat newly " "created file '%s'"), path); @@ -4310,13 +4309,13 @@ static int add_index_file(struct apply_state *state, fill_stat_cache_info(ce, &st); } if (write_sha1_file(buf, size, blob_type, ce->oid.hash) < 0) { - free(ce); + cache_entry_free(ce); return error(_("unable to create backing store " "for newly created file %s"), path); } } if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0) { - free(ce); + cache_entry_free(ce); return error(_("unable to add cache entry for %s"), path); } @@ -4440,27 +4439,26 @@ static int add_conflicted_stages_file(struct apply_state *state, struct patch *patch) { int stage, namelen; - unsigned ce_size, mode; + unsigned mode; struct cache_entry *ce; if (!state->update_index) return 0; namelen = strlen(patch->new_name); - ce_size = cache_entry_size(namelen); mode = patch->new_mode ? patch->new_mode : (S_IFREG | 0644); remove_file_from_cache(patch->new_name); for (stage = 1; stage < 4; stage++) { if (is_null_oid(&patch->threeway_stage[stage - 1])) continue; - ce = xcalloc(1, ce_size); + ce = make_empty_cache_entry(namelen); memcpy(ce->name, patch->new_name, namelen); ce->ce_mode = create_ce_mode(mode); ce->ce_flags = create_ce_flags(stage); ce->ce_namelen = namelen; oidcpy(&ce->oid, &patch->threeway_stage[stage - 1]); if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0) { - free(ce); + cache_entry_free(ce); return error(_("unable to add cache entry for %s"), patch->new_name); } diff --git a/blame.c b/blame.c index 2893f3c1030aab..47c94d24ed012e 100644 --- a/blame.c +++ b/blame.c @@ -154,7 +154,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt, struct strbuf buf = STRBUF_INIT; const char *ident; time_t now; - int size, len; + int len; struct cache_entry *ce; unsigned mode; struct strbuf msg = STRBUF_INIT; @@ -252,8 +252,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt, /* Let's not bother reading from HEAD tree */ mode = S_IFREG | 0644; } - size = cache_entry_size(len); - ce = xcalloc(1, size); + ce = make_empty_cache_entry(len); oidcpy(&ce->oid, &origin->blob_oid); memcpy(ce->name, path, len); ce->ce_flags = create_ce_flags(0); diff --git a/builtin/checkout.c b/builtin/checkout.c index e2cfa7b0395297..9a3dd2ed489a38 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -82,7 +82,7 @@ static int update_some(const unsigned char *sha1, struct strbuf *base, return READ_TREE_RECURSIVE; len = base->len + strlen(pathname); - ce = xcalloc(1, cache_entry_size(len)); + ce = make_empty_cache_entry(len); hashcpy(ce->oid.hash, sha1); memcpy(ce->name, base->buf, base->len); memcpy(ce->name + base->len, pathname, len - base->len); @@ -101,7 +101,7 @@ static int update_some(const unsigned char *sha1, struct strbuf *base, if (ce->ce_mode == old->ce_mode && !oidcmp(&ce->oid, &old->oid)) { old->ce_flags |= CE_UPDATE; - free(ce); + cache_entry_free(ce); return 0; } } @@ -236,11 +236,11 @@ static int checkout_merged(int pos, const struct checkout *state) blob_type, oid.hash)) die(_("Unable to add merge result for '%s'"), path); free(result_buf.ptr); - ce = make_cache_entry(mode, oid.hash, path, 2, 0); + ce = make_transient_cache_entry(mode, oid.hash, path, 2); if (!ce) die(_("make_cache_entry failed for path '%s'"), path); status = checkout_entry(ce, state, NULL); - free(ce); + transient_cache_entry_free(ce); return status; } diff --git a/builtin/difftool.c b/builtin/difftool.c index bcc79d1888f221..c29be7b9fa60fa 100644 --- a/builtin/difftool.c +++ b/builtin/difftool.c @@ -321,10 +321,10 @@ static int checkout_path(unsigned mode, struct object_id *oid, struct cache_entry *ce; int ret; - ce = make_cache_entry(mode, oid->hash, path, 0, 0); + ce = make_transient_cache_entry(mode, oid->hash, path, 0); ret = checkout_entry(ce, state, NULL); - free(ce); + transient_cache_entry_free(ce); return ret; } @@ -488,7 +488,7 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix, * index. */ struct cache_entry *ce2 = - make_cache_entry(rmode, roid.hash, + make_cache_entry_from_index(&wtindex, rmode, roid.hash, dst_path, 0, 0); add_index_entry(&wtindex, ce2, diff --git a/builtin/reset.c b/builtin/reset.c index d09bc19beedc48..599ba8df984749 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -164,10 +164,10 @@ static void update_index_from_diff(struct diff_queue_struct *q, state.force = 1; state.refresh_cache = 1; state.istate = &the_index; - ceBefore = make_cache_entry(two->mode, two->oid.hash, two->path, + ceBefore = make_cache_entry_from_index(&the_index, two->mode, two->oid.hash, two->path, 0, 0); if (!ceBefore) - die(_("make_cache_entry failed for path '%s'"), + die(_("make_cache_entry_from_index failed for path '%s'"), two->path); checkout_entry(ceBefore, &state, NULL); @@ -179,10 +179,10 @@ static void update_index_from_diff(struct diff_queue_struct *q, continue; } - ce = make_cache_entry(one->mode, one->oid.hash, one->path, + ce = make_cache_entry_from_index(&the_index, one->mode, one->oid.hash, one->path, 0, 0); if (!ce) - die(_("make_cache_entry failed for path '%s'"), + die(_("make_cache_entry_from_index failed for path '%s'"), one->path); if (is_missing) { ce->ce_flags |= CE_INTENT_TO_ADD; diff --git a/builtin/update-index.c b/builtin/update-index.c index 58d1c2d2827d61..8638169f055aef 100644 --- a/builtin/update-index.c +++ b/builtin/update-index.c @@ -268,15 +268,14 @@ static int process_lstat_error(const char *path, int err) static int add_one_path(const struct cache_entry *old, const char *path, int len, struct stat *st) { - int option, size; + int option; struct cache_entry *ce; /* Was the old index entry already up-to-date? */ if (old && !ce_stage(old) && !ce_match_stat(old, st, 0)) return 0; - size = cache_entry_size(len); - ce = xcalloc(1, size); + ce = make_empty_cache_entry(len); memcpy(ce->name, path, len); ce->ce_flags = create_ce_flags(0); ce->ce_namelen = len; @@ -285,13 +284,13 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len if (index_path(&ce->oid, path, st, info_only ? 0 : HASH_WRITE_OBJECT)) { - free(ce); + cache_entry_free(ce); return -1; } option = allow_add ? ADD_CACHE_OK_TO_ADD : 0; option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0; if (add_cache_entry(ce, option)) { - free(ce); + cache_entry_free(ce); return error("%s: cannot add to the index - missing --add option?", path); } return 0; @@ -403,15 +402,14 @@ static int process_path(const char *path) static int add_cacheinfo(unsigned int mode, const struct object_id *oid, const char *path, int stage) { - int size, len, option; + int len, option; struct cache_entry *ce; if (!verify_path(path)) return error("Invalid path '%s'", path); len = strlen(path); - size = cache_entry_size(len); - ce = xcalloc(1, size); + ce = make_empty_cache_entry(len); oidcpy(&ce->oid, oid); memcpy(ce->name, path, len); @@ -589,7 +587,6 @@ static struct cache_entry *read_one_ent(const char *which, { unsigned mode; struct object_id oid; - int size; struct cache_entry *ce; if (get_tree_entry(ent->hash, path, oid.hash, &mode)) { @@ -602,8 +599,7 @@ static struct cache_entry *read_one_ent(const char *which, error("%s: not a blob in %s branch.", path, which); return NULL; } - size = cache_entry_size(namelen); - ce = xcalloc(1, size); + ce = make_empty_cache_entry(namelen); oidcpy(&ce->oid, &oid); memcpy(ce->name, path, namelen); @@ -680,8 +676,8 @@ static int unresolve_one(const char *path) error("%s: cannot add their version to the index.", path); ret = -1; free_return: - free(ce_2); - free(ce_3); + cache_entry_free(ce_2); + cache_entry_free(ce_3); return ret; } @@ -748,7 +744,7 @@ static int do_reupdate(int ac, const char **av, ce->name, ce_namelen(ce), 0); if (old && ce->ce_mode == old->ce_mode && !oidcmp(&ce->oid, &old->oid)) { - free(old); + cache_entry_free(old); continue; /* unchanged */ } /* Be careful. The working tree may not have the @@ -759,7 +755,7 @@ static int do_reupdate(int ac, const char **av, path = xstrdup(ce->name); update_one(path); free(path); - free(old); + cache_entry_free(old); if (save_nr != active_nr) goto redo; } diff --git a/cache.h b/cache.h index 9b45603a669672..04de11c7b3ad47 100644 --- a/cache.h +++ b/cache.h @@ -10,6 +10,7 @@ #include "convert.h" #include "trace.h" #include "string-list.h" +#include "mem-pool.h" #include "pack-revindex.h" #include "hash.h" #include "path.h" @@ -353,6 +354,7 @@ struct index_state { struct untracked_cache *untracked; uint64_t fsmonitor_last_update; struct ewah_bitmap *fsmonitor_dirty; + struct mem_pool *ce_mem_pool; }; extern struct index_state the_index; @@ -396,6 +398,7 @@ extern void free_name_hash(struct index_state *istate); #define unmerge_cache_entry_at(at) unmerge_index_entry_at(&the_index, at) #define unmerge_cache(pathspec) unmerge_index(&the_index, pathspec) #define read_blob_data_from_cache(path, sz) read_blob_data_from_index(&the_index, (path), (sz)) +#define make_empty_cache_entry(len) make_empty_cache_entry_from_index(&the_index, len) #endif enum object_type { @@ -712,7 +715,25 @@ extern int remove_file_from_index(struct index_state *, const char *path); extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags); extern int add_file_to_index(struct index_state *, const char *path, int flags); -extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, unsigned int refresh_options); +extern struct cache_entry *make_cache_entry_from_index(struct index_state *, unsigned int mode, const unsigned char *sha1, const char *path, int stage, unsigned int refresh_options); +extern struct cache_entry *make_transient_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage); + +/* + * Create an empty cache entry struct. This is intended for use with + * cache_entries that could be added to the associated index. + * + * istate: The index whose memory pool this cache entry should be allocated from. + * len: The length to reserve for the path field of the cache entry. + */ +extern struct cache_entry *make_empty_cache_entry_from_index(struct index_state *istate, size_t len); + +/* + * Create an empty cache entry struct. This is intended for use with + * cache_entries that will not be added to an index. + * + * len: The length to reserve for the path field of the cache entry. + */ +extern struct cache_entry *make_empty_transient_cache_entry(size_t len); extern int chmod_index_entry(struct index_state *, struct cache_entry *ce, char flip); extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b); extern void set_object_name_for_intent_to_add_entry(struct cache_entry *ce); @@ -765,7 +786,7 @@ extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st); #define REFRESH_IGNORE_SUBMODULES 0x0010 /* ignore submodules */ #define REFRESH_IN_PORCELAIN 0x0020 /* user friendly output, not "needs update" */ extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg); -extern struct cache_entry *refresh_cache_entry(struct cache_entry *, unsigned int); +extern struct cache_entry *refresh_cache_entry(struct index_state *, struct cache_entry *, unsigned int); /* * Opportunistically update the index but do not complain if we can't. @@ -1988,4 +2009,9 @@ void safe_create_dir(const char *dir, int share); */ extern int print_sha1_ellipsis(void); +void cache_entry_free(struct cache_entry *ce); +void transient_cache_entry_free(struct cache_entry *ce); + +void validate_cache_entries(const struct index_state *istate); + #endif /* CACHE_H */ diff --git a/fast-import.c b/fast-import.c index 04023e7c5fda52..e7da955e161843 100644 --- a/fast-import.c +++ b/fast-import.c @@ -209,13 +209,6 @@ struct last_object { unsigned no_swap : 1; }; -struct mem_pool { - struct mem_pool *next_pool; - char *next_free; - char *end; - uintmax_t space[FLEX_ARRAY]; /* more */ -}; - struct atom_str { struct atom_str *next_atom; unsigned short str_len; @@ -304,9 +297,7 @@ static int global_argc; static const char **global_argv; /* Memory pools */ -static size_t mem_pool_alloc = 2*1024*1024 - sizeof(struct mem_pool); -static size_t total_allocd; -static struct mem_pool *mem_pool; +static struct mem_pool mem_pool = {0, 2 * 1024 * 1024, 0 }; /* Atom management */ static unsigned int atom_table_sz = 4451; @@ -324,6 +315,7 @@ static off_t pack_size; /* Table of objects we've written. */ static unsigned int object_entry_alloc = 5000; static struct object_entry_pool *blocks; +static size_t total_allocd = 0; static struct object_entry *object_table[1 << 16]; static struct mark_set *marks; static const char *export_marks_file; @@ -636,41 +628,12 @@ static unsigned int hc_str(const char *s, size_t len) static void *pool_alloc(size_t len) { - struct mem_pool *p; - void *r; - - /* round up to a 'uintmax_t' alignment */ - if (len & (sizeof(uintmax_t) - 1)) - len += sizeof(uintmax_t) - (len & (sizeof(uintmax_t) - 1)); - - for (p = mem_pool; p; p = p->next_pool) - if ((p->end - p->next_free >= len)) - break; - - if (!p) { - if (len >= (mem_pool_alloc/2)) { - total_allocd += len; - return xmalloc(len); - } - total_allocd += sizeof(struct mem_pool) + mem_pool_alloc; - p = xmalloc(st_add(sizeof(struct mem_pool), mem_pool_alloc)); - p->next_pool = mem_pool; - p->next_free = (char *) p->space; - p->end = p->next_free + mem_pool_alloc; - mem_pool = p; - } - - r = p->next_free; - p->next_free += len; - return r; + return mem_pool_alloc(&mem_pool, len); } static void *pool_calloc(size_t count, size_t size) { - size_t len = count * size; - void *r = pool_alloc(len); - memset(r, 0, len); - return r; + return mem_pool_calloc(&mem_pool, count, size); } static char *pool_strdup(const char *s) @@ -3539,8 +3502,8 @@ int cmd_main(int argc, const char **argv) fprintf(stderr, "Total branches: %10lu (%10lu loads )\n", branch_count, branch_load_count); fprintf(stderr, " marks: %10" PRIuMAX " (%10" PRIuMAX " unique )\n", (((uintmax_t)1) << marks->shift) * 1024, marks_set_count); fprintf(stderr, " atoms: %10u\n", atom_cnt); - fprintf(stderr, "Memory total: %10" PRIuMAX " KiB\n", (total_allocd + alloc_count*sizeof(struct object_entry))/1024); - fprintf(stderr, " pools: %10lu KiB\n", (unsigned long)(total_allocd/1024)); + fprintf(stderr, "Memory total: %10" PRIuMAX " KiB\n", ((total_allocd + mem_pool.total_allocd) + alloc_count*sizeof(struct object_entry))/1024); + fprintf(stderr, " pools: %10lu KiB\n", (unsigned long)((total_allocd + mem_pool.total_allocd) /1024)); fprintf(stderr, " objects: %10" PRIuMAX " KiB\n", (alloc_count*sizeof(struct object_entry))/1024); fprintf(stderr, "---------------------------------------------------------------------\n"); pack_report(); diff --git a/git.c b/git.c index 745c7336224c69..5a95ba39548cca 100644 --- a/git.c +++ b/git.c @@ -406,7 +406,14 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv) trace_argv_printf(argv, "trace: built-in: git"); + /* + * Validate the state of the cache entries in the index before and + * after running the command. Validation is only performed if the + * appropriate environment variable is set. + */ + validate_cache_entries(&the_index); exit_code = status = p->fn(argc, argv, prefix); + validate_cache_entries(&the_index); if (status) return status; diff --git a/mem-pool.c b/mem-pool.c new file mode 100644 index 00000000000000..2b2b134d67d178 --- /dev/null +++ b/mem-pool.c @@ -0,0 +1,143 @@ +/* + * Memory pool management. + */ + +#include "cache.h" +#include "mem-pool.h" + +#define MIN_ALLOC_GROWTH_SIZE 1024 * 1024 + +struct mp_block { + struct mp_block *next_block; + char *next_free; + char *end; + uintmax_t space[FLEX_ARRAY]; /* more */ +}; + +static struct mp_block *mem_pool_alloc_block(struct mem_pool *mem_pool, size_t alloc_size) +{ + struct mp_block *p; + mem_pool->total_allocd += alloc_size; + + p = xmalloc(st_add(sizeof(struct mp_block), alloc_size)); + + p->next_block = mem_pool->mp_block; + p->next_free = (char *)p->space; + p->end = p->next_free + alloc_size; + mem_pool->mp_block = p; + + return p; +} + +void mem_pool_init(struct mem_pool **mem_pool, size_t alloc_growth_size, size_t initial_size) +{ + if (!(*mem_pool)) + { + if (alloc_growth_size < MIN_ALLOC_GROWTH_SIZE) + alloc_growth_size = MIN_ALLOC_GROWTH_SIZE; + + *mem_pool = xmalloc(sizeof(struct mem_pool)); + (*mem_pool)->total_allocd = 0; + (*mem_pool)->mp_block = 0; + (*mem_pool)->alloc_size = alloc_growth_size; + + if (initial_size > 0) + mem_pool_alloc_block((*mem_pool), initial_size); + } +} + +void mem_pool_combine(struct mem_pool *dst, struct mem_pool *src) +{ + struct mp_block *p, *next_block; + for (next_block = src->mp_block; next_block;) { + p = next_block; + next_block = next_block->next_block; + p->next_block = dst->mp_block; + dst->mp_block = p; + } + + src->mp_block = NULL; +} + +void mem_pool_discard(struct mem_pool *mem_pool) +{ + struct mp_block *block, *block_to_free; + int invalidate_memory = should_validate_cache_entries(); + + for (block = mem_pool->mp_block; block;) + { + block_to_free = block; + block = block->next_block; + + if (invalidate_memory) + memset(block_to_free->space, 0xCD, ((char *)block_to_free->end) - ((char *)block_to_free->space)); + + free(block_to_free); + } + + free(mem_pool); +} + +int should_validate_cache_entries(void) +{ + static int validate_index_cache_entries = -1; + + if (validate_index_cache_entries < 0) { + if (getenv("GIT_TEST_VALIDATE_INDEX_CACHE_ENTRIES")) + validate_index_cache_entries = 1; + else + validate_index_cache_entries = 0; + } + + return validate_index_cache_entries; +} + +void *mem_pool_alloc(struct mem_pool *mem_pool, size_t len) +{ + struct mp_block *p; + void *r; + + /* Round up to a 'uintmax_t' alignment */ + if (len & (sizeof(uintmax_t) - 1)) + len += sizeof(uintmax_t) - (len & (sizeof(uintmax_t) - 1)); + + p = mem_pool->mp_block; + + if (p && + (p->end - p->next_free < len)) { + for (p = p->next_block; p; p = p->next_block) + if (p->end - p->next_free >= len) + break; + } + + if (!p) { + if (len >= ((mem_pool->alloc_size - sizeof(struct mp_block)) / 2)) { + p = mem_pool_alloc_block(mem_pool, len); + } + else + p = mem_pool_alloc_block(mem_pool, mem_pool->alloc_size); + } + + r = p->next_free; + p->next_free += len; + return r; +} + +int mem_pool_contains(struct mem_pool *mem_pool, void *mem) +{ + struct mp_block *p; + for (p = mem_pool->mp_block; p; p = p->next_block) + if ((mem >= ((void *)p->space)) && + (mem < ((void *)p->end))) + return 1; + + return 0; +} + +void *mem_pool_calloc(struct mem_pool *mem_pool, size_t count, size_t size) +{ + size_t len = count * size; + void *r = mem_pool_alloc(mem_pool, len); + memset(r, 0, len); + return r; +} diff --git a/mem-pool.h b/mem-pool.h new file mode 100644 index 00000000000000..65fa55fd25fc5d --- /dev/null +++ b/mem-pool.h @@ -0,0 +1,20 @@ +#ifndef MEM_POOL_H +#define MEM_POOL_H + +struct mem_pool { + struct mp_block *mp_block; + size_t alloc_size; + size_t total_allocd; +}; + +void mem_pool_init(struct mem_pool **mem_pool, size_t alloc_growth_size, size_t initial_size); +void mem_pool_combine(struct mem_pool *dst, struct mem_pool *src); +void mem_pool_discard(struct mem_pool *mem_pool); + +void *mem_pool_alloc(struct mem_pool *pool, size_t len); +void *mem_pool_calloc(struct mem_pool *pool, size_t count, size_t size); +int mem_pool_contains(struct mem_pool *mem_pool, void *mem); + +int should_validate_cache_entries(void); + +#endif diff --git a/merge-recursive.c b/merge-recursive.c index 970033c5da66d9..297c0f80e980fe 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -253,7 +253,7 @@ static int add_cacheinfo(struct merge_options *o, struct cache_entry *ce; int ret; - ce = make_cache_entry(mode, oid ? oid->hash : null_sha1, path, stage, 0); + ce = make_cache_entry_from_index(&the_index, mode, oid ? oid->hash : null_sha1, path, stage, 0); if (!ce) return err(o, _("addinfo_cache failed for path '%s'"), path); @@ -261,7 +261,7 @@ static int add_cacheinfo(struct merge_options *o, if (refresh) { struct cache_entry *nce; - nce = refresh_cache_entry(ce, CE_MATCH_REFRESH | CE_MATCH_IGNORE_MISSING); + nce = refresh_cache_entry(&the_index, ce, CE_MATCH_REFRESH | CE_MATCH_IGNORE_MISSING); if (!nce) return err(o, _("addinfo_cache failed for path '%s'"), path); if (nce != ce) diff --git a/read-cache.c b/read-cache.c index 2d22d4529e8181..7ddf9b6b2f00e7 100644 --- a/read-cache.c +++ b/read-cache.c @@ -1,4 +1,4 @@ -/* + /* * GIT - The information manager from hell * * Copyright (C) Linus Torvalds, 2005 @@ -50,6 +50,38 @@ struct index_state the_index; static const char *alternate_index_output; +/* + * Number of characters to set aside for each cache entry + * path. If the initial amount of memory set aside is not + * sufficient, the mem pool will allocate extra memory. + */ +#define CACHE_ENTRY_PATH_LENGTH 80 + +static struct cache_entry *mem_pool__ce_alloc(struct mem_pool *mem_pool, size_t len) +{ + return mem_pool_alloc(mem_pool, cache_entry_size(len)); +} + +static struct cache_entry *mem_pool__ce_calloc(struct mem_pool *mem_pool, size_t len) +{ + return mem_pool_calloc(mem_pool, 1, cache_entry_size(len)); +} + +static struct mem_pool *find_mem_pool(struct index_state *istate) +{ + struct mem_pool **pool_ptr; + + if (istate->split_index && istate->split_index->base) + pool_ptr = &istate->split_index->base->ce_mem_pool; + else + pool_ptr = &istate->ce_mem_pool; + + if (!*pool_ptr) + mem_pool_init(pool_ptr, 0, 0); + + return *pool_ptr; +} + static void set_index_entry(struct index_state *istate, int nr, struct cache_entry *ce) { istate->cache[nr] = ce; @@ -62,7 +94,7 @@ static void replace_index_entry(struct index_state *istate, int nr, struct cache replace_index_entry_in_base(istate, old, ce); remove_name_hash(istate, old); - free(old); + cache_entry_free(old); set_index_entry(istate, nr, ce); ce->ce_flags |= CE_UPDATE_IN_BASE; mark_fsmonitor_invalid(istate, ce); @@ -74,7 +106,7 @@ void rename_index_entry_at(struct index_state *istate, int nr, const char *new_n struct cache_entry *old = istate->cache[nr], *new; int namelen = strlen(new_name); - new = xmalloc(cache_entry_size(namelen)); + new = mem_pool__ce_alloc(find_mem_pool(istate), namelen); copy_cache_entry(new, old); new->ce_flags &= ~CE_HASHED; new->ce_namelen = namelen; @@ -623,7 +655,7 @@ static struct cache_entry *create_alias_ce(struct index_state *istate, /* Ok, create the new entry using the name of the existing alias */ len = ce_namelen(alias); - new = xcalloc(1, cache_entry_size(len)); + new = make_empty_cache_entry_from_index(istate, len); memcpy(new->name, alias->name, len); copy_cache_entry(new, ce); save_or_free_index_entry(istate, ce); @@ -640,7 +672,7 @@ void set_object_name_for_intent_to_add_entry(struct cache_entry *ce) int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags) { - int size, namelen, was_same; + int namelen, was_same; mode_t st_mode = st->st_mode; struct cache_entry *ce, *alias = NULL; unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE|CE_MATCH_RACY_IS_DIRTY; @@ -662,8 +694,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st, while (namelen && path[namelen-1] == '/') namelen--; } - size = cache_entry_size(namelen); - ce = xcalloc(1, size); + ce = make_empty_cache_entry_from_index(istate, namelen); memcpy(ce->name, path, namelen); ce->ce_namelen = namelen; if (!intent_only) @@ -704,13 +735,13 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st, ce_mark_uptodate(alias); alias->ce_flags |= CE_ADDED; - free(ce); + cache_entry_free(ce); return 0; } } if (!intent_only) { if (index_path(&ce->oid, path, st, newflags)) { - free(ce); + cache_entry_free(ce); return error("unable to index file %s", path); } } else @@ -727,9 +758,9 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st, ce->ce_mode == alias->ce_mode); if (pretend) - free(ce); + cache_entry_free(ce); else if (add_index_entry(istate, ce, add_option)) { - free(ce); + cache_entry_free(ce); return error("unable to add %s to index", path); } if (verbose && !was_same) @@ -745,11 +776,22 @@ int add_file_to_index(struct index_state *istate, const char *path, int flags) return add_to_index(istate, path, &st, flags); } -struct cache_entry *make_cache_entry(unsigned int mode, - const unsigned char *sha1, const char *path, int stage, - unsigned int refresh_options) +struct cache_entry *make_empty_cache_entry_from_index(struct index_state *istate, size_t len) +{ + return mem_pool__ce_calloc(find_mem_pool(istate), len); +} + +struct cache_entry *make_empty_transient_cache_entry(size_t len) { - int size, len; + return xcalloc(1, cache_entry_size(len)); +} + +struct cache_entry *make_cache_entry(struct index_state *istate, + unsigned int mode, + const unsigned char *sha1, const char *path, int stage, + unsigned int refresh_options, int is_transient) +{ + int len; struct cache_entry *ce, *ret; if (!verify_path(path)) { @@ -758,8 +800,8 @@ struct cache_entry *make_cache_entry(unsigned int mode, } len = strlen(path); - size = cache_entry_size(len); - ce = xcalloc(1, size); + ce = is_transient ? make_empty_transient_cache_entry(len) : + make_empty_cache_entry_from_index(istate, len); hashcpy(ce->oid.hash, sha1); memcpy(ce->name, path, len); @@ -767,12 +809,36 @@ struct cache_entry *make_cache_entry(unsigned int mode, ce->ce_namelen = len; ce->ce_mode = create_ce_mode(mode); - ret = refresh_cache_entry(ce, refresh_options); - if (ret != ce) - free(ce); + /* + * Transient cache entries cannot be refreshed - they are not associated + * with the index. + */ + if (!is_transient) { + ret = refresh_cache_entry(istate, ce, refresh_options); + + if (ret != ce) + cache_entry_free(ce); + } else { + ret = ce; + } + return ret; } +struct cache_entry *make_cache_entry_from_index(struct index_state *istate, unsigned int mode, + const unsigned char *sha1, const char *path, + int stage, unsigned int refresh_options) +{ + return make_cache_entry(istate, mode, sha1, path, stage, + refresh_options, 0); +} + +struct cache_entry *make_transient_cache_entry(unsigned int mode, const unsigned char *sha1, + const char *path, int stage) +{ + return make_cache_entry(NULL, mode, sha1, path, stage, 0, 1); +} + /* * Chmod an index entry with either +x or -x. * @@ -1202,6 +1268,8 @@ int add_index_entry(struct index_state *istate, struct cache_entry *ce, int opti { int pos; + validate_cache_entries(istate); + if (option & ADD_CACHE_JUST_APPEND) pos = istate->cache_nr; else { @@ -1223,6 +1291,8 @@ int add_index_entry(struct index_state *istate, struct cache_entry *ce, int opti (istate->cache_nr - pos - 1) * sizeof(ce)); set_index_entry(istate, pos, ce); istate->cache_changed |= CE_ENTRY_ADDED; + + validate_cache_entries(istate); return 0; } @@ -1323,9 +1393,9 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate, *err = EINVAL; return NULL; } - + + updated = make_empty_cache_entry_from_index(istate, ce_namelen(ce)); size = ce_size(ce); - updated = xmalloc(size); memcpy(updated, ce, size); fill_stat_cache_info(updated, &st); /* @@ -1446,10 +1516,11 @@ int refresh_index(struct index_state *istate, unsigned int flags, return has_errors; } -struct cache_entry *refresh_cache_entry(struct cache_entry *ce, +struct cache_entry *refresh_cache_entry(struct index_state *istate, + struct cache_entry *ce, unsigned int options) { - return refresh_cache_ent(&the_index, ce, options, NULL, NULL); + return refresh_cache_ent(istate, ce, options, NULL, NULL); } @@ -1611,12 +1682,13 @@ int read_index(struct index_state *istate) return read_index_from(istate, get_index_file(), get_git_dir()); } -static struct cache_entry *cache_entry_from_ondisk(struct ondisk_cache_entry *ondisk, +static struct cache_entry *cache_entry_from_ondisk(struct mem_pool *mem_pool, + struct ondisk_cache_entry *ondisk, unsigned int flags, const char *name, size_t len) { - struct cache_entry *ce = xmalloc(cache_entry_size(len)); + struct cache_entry *ce = mem_pool__ce_alloc(mem_pool, len); ce->ce_stat_data.sd_ctime.sec = get_be32(&ondisk->ctime.sec); ce->ce_stat_data.sd_mtime.sec = get_be32(&ondisk->mtime.sec); @@ -1658,7 +1730,8 @@ static unsigned long expand_name_field(struct strbuf *name, const char *cp_) return (const char *)ep + 1 - cp_; } -static struct cache_entry *create_from_disk(struct ondisk_cache_entry *ondisk, +static struct cache_entry *create_from_disk(struct mem_pool *mem_pool, + struct ondisk_cache_entry *ondisk, unsigned long *ent_size, struct strbuf *previous_name) { @@ -1689,13 +1762,13 @@ static struct cache_entry *create_from_disk(struct ondisk_cache_entry *ondisk, /* v3 and earlier */ if (len == CE_NAMEMASK) len = strlen(name); - ce = cache_entry_from_ondisk(ondisk, flags, name, len); + ce = cache_entry_from_ondisk(mem_pool, ondisk, flags, name, len); *ent_size = ondisk_ce_size(ce); } else { unsigned long consumed; consumed = expand_name_field(previous_name, name); - ce = cache_entry_from_ondisk(ondisk, flags, + ce = cache_entry_from_ondisk(mem_pool, ondisk, flags, previous_name->buf, previous_name->len); @@ -1769,6 +1842,17 @@ static void post_read_index_from(struct index_state *istate) tweak_fsmonitor(istate); } +static size_t estimate_cache_size(size_t ondisk_size, unsigned int entries) +{ + long per_entry = sizeof(struct cache_entry) - sizeof(struct ondisk_cache_entry); + + /* + * Account for potential alignment differences. + */ + per_entry += align_padding_size(sizeof(struct cache_entry), -sizeof(struct ondisk_cache_entry)); + return ondisk_size + entries * per_entry; +} + /* remember to discard_cache() before reading a different cache! */ int do_read_index(struct index_state *istate, const char *path, int must_exist) { @@ -1815,10 +1899,13 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist) istate->cache = xcalloc(istate->cache_alloc, sizeof(*istate->cache)); istate->initialized = 1; - if (istate->version == 4) + if (istate->version == 4) { previous_name = &previous_name_buf; - else + mem_pool_init(&istate->ce_mem_pool, 0, istate->cache_nr * (sizeof(struct cache_entry) + CACHE_ENTRY_PATH_LENGTH)); + } else { previous_name = NULL; + mem_pool_init(&istate->ce_mem_pool, 0, estimate_cache_size(mmap_size, istate->cache_nr)); + } src_offset = sizeof(*hdr); for (i = 0; i < istate->cache_nr; i++) { @@ -1827,7 +1914,7 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist) unsigned long consumed; disk_ce = (struct ondisk_cache_entry *)((char *)mmap + src_offset); - ce = create_from_disk(disk_ce, &consumed, previous_name); + ce = create_from_disk(istate->ce_mem_pool, disk_ce, &consumed, previous_name); set_index_entry(istate, i, ce); src_offset += consumed; @@ -1921,17 +2008,8 @@ int is_index_unborn(struct index_state *istate) int discard_index(struct index_state *istate) { - int i; + validate_cache_entries(istate); - for (i = 0; i < istate->cache_nr; i++) { - if (istate->cache[i]->index && - istate->split_index && - istate->split_index->base && - istate->cache[i]->index <= istate->split_index->base->cache_nr && - istate->cache[i] == istate->split_index->base->cache[istate->cache[i]->index - 1]) - continue; - free(istate->cache[i]); - } resolve_undo_clear_index(istate); istate->cache_nr = 0; istate->cache_changed = 0; @@ -1945,9 +2023,52 @@ int discard_index(struct index_state *istate) discard_split_index(istate); free_untracked_cache(istate->untracked); istate->untracked = NULL; + + if (istate->ce_mem_pool) { + mem_pool_discard(istate->ce_mem_pool); + istate->ce_mem_pool = NULL; + } return 0; } + +/* + * Validate the cache entries of this index. + * All cache entries associated with this index + * should have been allocated by the memory pool + * associated with this index, or by a referenced + * split index. + */ +void validate_cache_entries(const struct index_state *istate) +{ + int i; + int validate_index_cache_entries = should_validate_cache_entries(); + + if (!validate_index_cache_entries) + return; + + if (!istate || !istate->initialized) + return; + + for (i = 0; i < istate->cache_nr; i++) { + if (!istate) { + die("internal error: cache entry is not allocated from expected memory pool"); + } else if (!istate->ce_mem_pool || + !mem_pool_contains(istate->ce_mem_pool, istate->cache[i])) { + if (!istate->split_index || + !istate->split_index->base || + !istate->split_index->base || + !istate->split_index->base->ce_mem_pool || + !mem_pool_contains(istate->split_index->base->ce_mem_pool, istate->cache[i])) { + die("internal error: cache entry is not allocated from expected memory pool"); + } + } + } + + if (istate->split_index) + validate_cache_entries(istate->split_index->base); +} + int unmerged_index(const struct index_state *istate) { int i; @@ -2613,14 +2734,13 @@ int read_index_unmerged(struct index_state *istate) for (i = 0; i < istate->cache_nr; i++) { struct cache_entry *ce = istate->cache[i]; struct cache_entry *new_ce; - int size, len; + int len; if (!ce_stage(ce)) continue; unmerged = 1; len = ce_namelen(ce); - size = cache_entry_size(len); - new_ce = xcalloc(1, size); + new_ce = make_empty_cache_entry_from_index(istate, len); memcpy(new_ce->name, ce->name, len); new_ce->ce_flags = create_ce_flags(0) | CE_CONFLICTED; new_ce->ce_namelen = len; @@ -2729,3 +2849,19 @@ void move_index_extensions(struct index_state *dst, struct index_state *src) dst->untracked = src->untracked; src->untracked = NULL; } + +/* + * Indicate that a cache entry is no longer is use + */ +void cache_entry_free(struct cache_entry *ce) +{ + int invalidate_cache_entry = should_validate_cache_entries(); + + if (ce && invalidate_cache_entry) + memset(ce, 0xCD, cache_entry_size(ce->ce_namelen)); +} + +void transient_cache_entry_free(struct cache_entry *ce) +{ + free(ce); +} diff --git a/resolve-undo.c b/resolve-undo.c index b40f3173d3fe5e..0b37e580c17540 100644 --- a/resolve-undo.c +++ b/resolve-undo.c @@ -145,7 +145,7 @@ int unmerge_index_entry_at(struct index_state *istate, int pos) struct cache_entry *nce; if (!ru->mode[i]) continue; - nce = make_cache_entry(ru->mode[i], ru->sha1[i], + nce = make_cache_entry_from_index(istate, ru->mode[i], ru->sha1[i], name, i + 1, 0); if (matched) nce->ce_flags |= CE_MATCHED; diff --git a/split-index.c b/split-index.c index 284d04d67f885d..10fc676b53eda1 100644 --- a/split-index.c +++ b/split-index.c @@ -77,6 +77,20 @@ void move_cache_to_base_index(struct index_state *istate) * with istate->cache[]. Accept a bit of leaking here because * this code is only used by short-lived update-index. */ + + /* + * If there was a previous base index, then transfer ownership of allocated + * entries to the parent index. + */ + if (si->base && + si->base->ce_mem_pool) { + + if (!istate->ce_mem_pool) + mem_pool_init(&istate->ce_mem_pool, 0, 0); + + mem_pool_combine(istate->ce_mem_pool, istate->split_index->base->ce_mem_pool); + } + si->base = xcalloc(1, sizeof(*si->base)); si->base->version = istate->version; /* zero timestamp disables racy test in ce_write_index() */ @@ -84,6 +98,13 @@ void move_cache_to_base_index(struct index_state *istate) ALLOC_GROW(si->base->cache, istate->cache_nr, si->base->cache_alloc); si->base->cache_nr = istate->cache_nr; COPY_ARRAY(si->base->cache, istate->cache, istate->cache_nr); + + /* + * The mem_pool needs to move with the allocated entries. + */ + si->base->ce_mem_pool = istate->ce_mem_pool; + istate->ce_mem_pool = NULL; + mark_base_index_entries(si->base); for (i = 0; i < si->base->cache_nr; i++) si->base->cache[i]->ce_flags &= ~CE_UPDATE_IN_BASE; @@ -123,7 +144,12 @@ static void replace_entry(size_t pos, void *data) src->ce_flags |= CE_UPDATE_IN_BASE; src->ce_namelen = dst->ce_namelen; copy_cache_entry(dst, src); - free(src); + + /* + * N.B. src comes from the saved_cache, which was allocated + * from the original istate's mem_pool. + */ + cache_entry_free(src); si->nr_replacements++; } @@ -224,7 +250,7 @@ void prepare_to_write_split_index(struct index_state *istate) base->ce_flags = base_flags; if (ret) ce->ce_flags |= CE_UPDATE_IN_BASE; - free(base); + cache_entry_free(base); si->base->cache[ce->index - 1] = ce; } for (i = 0; i < si->base->cache_nr; i++) { @@ -301,7 +327,7 @@ void save_or_free_index_entry(struct index_state *istate, struct cache_entry *ce ce == istate->split_index->base->cache[ce->index - 1]) ce->ce_flags |= CE_REMOVE; else - free(ce); + cache_entry_free(ce); } void replace_index_entry_in_base(struct index_state *istate, @@ -314,7 +340,7 @@ void replace_index_entry_in_base(struct index_state *istate, old->index <= istate->split_index->base->cache_nr) { new->index = old->index; if (old != istate->split_index->base->cache[new->index - 1]) - free(istate->split_index->base->cache[new->index - 1]); + cache_entry_free(istate->split_index->base->cache[new->index - 1]); istate->split_index->base->cache[new->index - 1] = new; } } @@ -330,6 +356,7 @@ void add_split_index(struct index_state *istate) void remove_split_index(struct index_state *istate) { if (istate->split_index) { + mem_pool_combine(istate->ce_mem_pool, istate->split_index->base->ce_mem_pool); /* * can't discard_split_index(&the_index); because that * will destroy split_index->base->cache[], which may diff --git a/tree.c b/tree.c index b224115e0f4d61..9c7ad7833811b1 100644 --- a/tree.c +++ b/tree.c @@ -16,15 +16,13 @@ static int read_one_entry_opt(struct index_state *istate, unsigned mode, int stage, int opt) { int len; - unsigned int size; struct cache_entry *ce; if (S_ISDIR(mode)) return READ_TREE_RECURSIVE; len = strlen(pathname); - size = cache_entry_size(baselen + len); - ce = xcalloc(1, size); + ce = make_empty_cache_entry_from_index(istate, baselen + len); ce->ce_mode = create_ce_mode(mode); ce->ce_flags = create_ce_flags(stage); diff --git a/unpack-trees.c b/unpack-trees.c index 420566b5db4266..bdb4c4423ba1ac 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -204,10 +204,10 @@ static int do_add_entry(struct unpack_trees_options *o, struct cache_entry *ce, ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE); } -static struct cache_entry *dup_entry(const struct cache_entry *ce) +static struct cache_entry *dup_entry(const struct cache_entry *ce, struct index_state *istate) { unsigned int size = ce_size(ce); - struct cache_entry *new = xmalloc(size); + struct cache_entry *new = make_empty_cache_entry_from_index(istate, ce_namelen(ce)); memcpy(new, ce, size); return new; @@ -217,7 +217,7 @@ static void add_entry(struct unpack_trees_options *o, const struct cache_entry *ce, unsigned int set, unsigned int clear) { - do_add_entry(o, dup_entry(ce), set, clear); + do_add_entry(o, dup_entry(ce, &o->result), set, clear); } /* @@ -778,10 +778,17 @@ static int ce_in_traverse_path(const struct cache_entry *ce, return (info->pathlen < ce_namelen(ce)); } -static struct cache_entry *create_ce_entry(const struct traverse_info *info, const struct name_entry *n, int stage) +static struct cache_entry *create_ce_entry(const struct traverse_info *info, + const struct name_entry *n, + int stage, + struct index_state *istate, + int is_transient) { int len = traverse_path_len(info, n); - struct cache_entry *ce = xcalloc(1, cache_entry_size(len)); + struct cache_entry *ce = + is_transient ? + make_empty_transient_cache_entry(len) : + make_empty_cache_entry_from_index(istate, len); ce->ce_mode = create_ce_mode(n->mode); ce->ce_flags = create_ce_flags(stage); @@ -827,7 +834,7 @@ static int unpack_nondirectories(int n, unsigned long mask, stage = 3; else stage = 2; - src[i + o->merge] = create_ce_entry(info, names + i, stage); + src[i + o->merge] = create_ce_entry(info, names + i, stage, &o->result, o->merge); } if (o->merge) { @@ -836,7 +843,7 @@ static int unpack_nondirectories(int n, unsigned long mask, for (i = 0; i < n; i++) { struct cache_entry *ce = src[i + o->merge]; if (ce != o->df_conflict_entry) - free(ce); + transient_cache_entry_free(ce); } return rc; } @@ -1835,7 +1842,7 @@ static int merged_entry(const struct cache_entry *ce, struct unpack_trees_options *o) { int update = CE_UPDATE; - struct cache_entry *merge = dup_entry(ce); + struct cache_entry *merge = dup_entry(ce, &o->result); if (!old) { /* @@ -1855,7 +1862,7 @@ static int merged_entry(const struct cache_entry *ce, if (verify_absent(merge, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o)) { - free(merge); + cache_entry_free(merge); return -1; } invalidate_ce_path(merge, o); @@ -1881,7 +1888,7 @@ static int merged_entry(const struct cache_entry *ce, update = 0; } else { if (verify_uptodate(old, o)) { - free(merge); + cache_entry_free(merge); return -1; } /* Migrate old flags over */