summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/blame_git.c25
-rw-r--r--src/cache.c8
-rw-r--r--src/checkout.c4
-rw-r--r--src/common.h4
-rw-r--r--src/curl_stream.c14
-rw-r--r--src/diff.c25
-rw-r--r--src/filter.c7
-rw-r--r--src/global.c18
-rw-r--r--src/iterator.c4
-rw-r--r--src/khash.h2
-rw-r--r--src/merge.c6
-rw-r--r--src/openssl_stream.c7
-rw-r--r--src/rebase.c4
-rw-r--r--src/repository.c5
-rw-r--r--src/stash.c2
-rw-r--r--src/stransport_stream.c2
-rw-r--r--src/submodule.c4
-rw-r--r--src/thread-utils.c2
-rw-r--r--src/transports/http.c2
-rw-r--r--src/transports/smart_pkt.c2
-rw-r--r--src/transports/ssh.c4
-rw-r--r--src/transports/winhttp.c2
-rw-r--r--src/util.h13
-rw-r--r--src/win32/w32_crtdbg_stacktrace.c343
-rw-r--r--src/win32/w32_crtdbg_stacktrace.h93
-rw-r--r--src/win32/w32_stack.c192
-rw-r--r--src/win32/w32_stack.h138
-rw-r--r--src/xdiff/xdiff.h16
-rw-r--r--src/xdiff/xdiffi.c50
-rw-r--r--src/xdiff/xdiffi.h1
-rw-r--r--src/xdiff/xemit.c49
-rw-r--r--src/xdiff/xemit.h2
-rw-r--r--src/xdiff/xhistogram.c2
-rw-r--r--src/xdiff/xmerge.c4
-rw-r--r--src/xdiff/xpatience.c2
-rw-r--r--src/xdiff/xprepare.c21
-rw-r--r--src/xdiff/xutils.c42
-rw-r--r--src/xdiff/xutils.h1
38 files changed, 1004 insertions, 118 deletions
diff --git a/src/blame_git.c b/src/blame_git.c
index e863efe2e..f481426aa 100644
--- a/src/blame_git.c
+++ b/src/blame_git.c
@@ -304,21 +304,16 @@ static void blame_chunk(
}
static int my_emit(
- xdfenv_t *xe,
- xdchange_t *xscr,
- xdemitcb_t *ecb,
- xdemitconf_t const *xecfg)
+ long start_a, long count_a,
+ long start_b, long count_b,
+ void *cb_data)
{
- xdchange_t *xch = xscr;
- GIT_UNUSED(xe);
- GIT_UNUSED(xecfg);
- while (xch) {
- blame_chunk_cb_data *d = ecb->priv;
- blame_chunk(d->blame, d->tlno, d->plno, xch->i2, d->target, d->parent);
- d->plno = xch->i1 + xch->chg1;
- d->tlno = xch->i2 + xch->chg2;
- xch = xch->next;
- }
+ blame_chunk_cb_data *d = (blame_chunk_cb_data *)cb_data;
+
+ blame_chunk(d->blame, d->tlno, d->plno, start_b, d->target, d->parent);
+ d->plno = start_a + count_a;
+ d->tlno = start_b + count_b;
+
return 0;
}
@@ -352,7 +347,7 @@ static int diff_hunks(mmfile_t file_a, mmfile_t file_b, void *cb_data)
xdemitconf_t xecfg = {0};
xdemitcb_t ecb = {0};
- xecfg.emit_func = (void(*)(void))my_emit;
+ xecfg.hunk_func = my_emit;
ecb.priv = cb_data;
trim_common_tail(&file_a, &file_b, 0);
diff --git a/src/cache.c b/src/cache.c
index 2f3ad1563..ca5173c0d 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -50,16 +50,16 @@ void git_cache_dump_stats(git_cache *cache)
if (kh_size(cache->map) == 0)
return;
- printf("Cache %p: %d items cached, %d bytes\n",
- cache, kh_size(cache->map), (int)cache->used_memory);
+ printf("Cache %p: %d items cached, %"PRIdZ" bytes\n",
+ cache, kh_size(cache->map), cache->used_memory);
kh_foreach_value(cache->map, object, {
char oid_str[9];
- printf(" %s%c %s (%d)\n",
+ printf(" %s%c %s (%"PRIuZ")\n",
git_object_type2string(object->type),
object->flags == GIT_CACHE_STORE_PARSED ? '*' : ' ',
git_oid_tostr(oid_str, sizeof(oid_str), &object->oid),
- (int)object->size
+ object->size
);
});
}
diff --git a/src/checkout.c b/src/checkout.c
index a94d509d3..4b3acbcce 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -1299,8 +1299,8 @@ static int checkout_get_actions(
if (counts[CHECKOUT_ACTION__CONFLICT] > 0 &&
(data->strategy & GIT_CHECKOUT_ALLOW_CONFLICTS) == 0)
{
- giterr_set(GITERR_CHECKOUT, "%d %s checkout",
- (int)counts[CHECKOUT_ACTION__CONFLICT],
+ giterr_set(GITERR_CHECKOUT, "%"PRIuZ" %s checkout",
+ counts[CHECKOUT_ACTION__CONFLICT],
counts[CHECKOUT_ACTION__CONFLICT] == 1 ?
"conflict prevents" : "conflicts prevent");
error = GIT_ECONFLICT;
diff --git a/src/common.h b/src/common.h
index cdb0b514d..2b1dedc01 100644
--- a/src/common.h
+++ b/src/common.h
@@ -46,6 +46,10 @@
# ifdef GIT_THREADS
# include "win32/pthread.h"
# endif
+# if defined(GIT_MSVC_CRTDBG)
+# include "win32/w32_stack.h"
+# include "win32/w32_crtdbg_stacktrace.h"
+# endif
#else
diff --git a/src/curl_stream.c b/src/curl_stream.c
index 6534bdbbe..63421fcf7 100644
--- a/src/curl_stream.c
+++ b/src/curl_stream.c
@@ -67,9 +67,9 @@ static int curls_certificate(git_cert **out, git_stream *stream)
/* No information is available, can happen with SecureTransport */
if (certinfo->num_of_certs == 0) {
- s->cert_info.cert_type = GIT_CERT_NONE;
- s->cert_info.data = NULL;
- s->cert_info.len = 0;
+ s->cert_info.parent.cert_type = GIT_CERT_NONE;
+ s->cert_info.data = NULL;
+ s->cert_info.len = 0;
return 0;
}
@@ -85,11 +85,11 @@ static int curls_certificate(git_cert **out, git_stream *stream)
s->cert_info_strings.strings = (char **) strings.contents;
s->cert_info_strings.count = strings.length;
- s->cert_info.cert_type = GIT_CERT_STRARRAY;
- s->cert_info.data = &s->cert_info_strings;
- s->cert_info.len = strings.length;
+ s->cert_info.parent.cert_type = GIT_CERT_STRARRAY;
+ s->cert_info.data = &s->cert_info_strings;
+ s->cert_info.len = strings.length;
- *out = (git_cert *) &s->cert_info;
+ *out = &s->cert_info.parent;
return 0;
}
diff --git a/src/diff.c b/src/diff.c
index c1adcc662..44f627880 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -1421,6 +1421,31 @@ int git_diff_tree_to_workdir_with_index(
return error;
}
+int git_diff_index_to_index(
+ git_diff **diff,
+ git_repository *repo,
+ git_index *old_index,
+ git_index *new_index,
+ const git_diff_options *opts)
+{
+ int error = 0;
+
+ assert(diff && old_index && new_index);
+
+ DIFF_FROM_ITERATORS(
+ git_iterator_for_index(
+ &a, old_index, GIT_ITERATOR_DONT_IGNORE_CASE, pfx, pfx),
+ git_iterator_for_index(
+ &b, new_index, GIT_ITERATOR_DONT_IGNORE_CASE, pfx, pfx)
+ );
+
+ /* if index is in case-insensitive order, re-sort deltas to match */
+ if (!error && (old_index->ignore_case || new_index->ignore_case))
+ diff_set_ignore_case(*diff, true);
+
+ return error;
+}
+
size_t git_diff_num_deltas(const git_diff *diff)
{
assert(diff);
diff --git a/src/filter.c b/src/filter.c
index e25d37c35..70c4fa382 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -433,8 +433,11 @@ static int filter_list_check_attributes(
want_type = git_attr_value(want);
found_type = git_attr_value(strs[i]);
- if (want_type != found_type ||
- (want_type == GIT_ATTR_VALUE_T && strcmp(want, strs[i])))
+ if (want_type != found_type)
+ error = GIT_ENOTFOUND;
+ else if (want_type == GIT_ATTR_VALUE_T &&
+ strcmp(want, strs[i]) &&
+ strcmp(want, "*"))
error = GIT_ENOTFOUND;
}
diff --git a/src/global.c b/src/global.c
index cb2242405..37a47bd27 100644
--- a/src/global.c
+++ b/src/global.c
@@ -11,7 +11,10 @@
#include "git2/global.h"
#include "git2/sys/openssl.h"
#include "thread-utils.h"
-
+#if defined(GIT_MSVC_CRTDBG)
+#include "win32/w32_stack.h"
+#include "win32/w32_crtdbg_stacktrace.h"
+#endif
git_mutex git__mwindow_mutex;
@@ -225,6 +228,11 @@ int git_libgit2_init(void)
/* Only do work on a 0 -> 1 transition of the refcount */
if ((ret = git_atomic_inc(&git__n_inits)) == 1) {
+#if defined(GIT_MSVC_CRTDBG)
+ git_win32__crtdbg_stacktrace_init();
+ git_win32__stack_init();
+#endif
+
if (synchronized_threads_init() < 0)
ret = -1;
}
@@ -254,9 +262,15 @@ int git_libgit2_shutdown(void)
while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
/* Only do work on a 1 -> 0 transition of the refcount */
- if ((ret = git_atomic_dec(&git__n_inits)) == 0)
+ if ((ret = git_atomic_dec(&git__n_inits)) == 0) {
synchronized_threads_shutdown();
+#if defined(GIT_MSVC_CRTDBG)
+ git_win32__crtdbg_stacktrace_cleanup();
+ git_win32__stack_cleanup();
+#endif
+ }
+
/* Exit the lock */
InterlockedExchange(&_mutex, 0);
diff --git a/src/iterator.c b/src/iterator.c
index 2c3a87af0..a312afb3a 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -1921,8 +1921,8 @@ int git_iterator_walk(
}
done:
- git__free(iterator_item);
- git__free(cur_items);
+ git__free((git_index_entry **)iterator_item);
+ git__free((git_index_entry **)cur_items);
if (error == GIT_ITEROVER)
error = 0;
diff --git a/src/khash.h b/src/khash.h
index 818ac833b..71eb583d5 100644
--- a/src/khash.h
+++ b/src/khash.h
@@ -619,4 +619,4 @@ typedef const char *kh_cstr_t;
#define KHASH_MAP_INIT_STR(name, khval_t) \
KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
-#endif /* __AC_KHASH_H */ \ No newline at end of file
+#endif /* __AC_KHASH_H */
diff --git a/src/merge.c b/src/merge.c
index 9d6252ea8..863ac8f2d 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -79,7 +79,7 @@ int merge_bases_many(git_commit_list **out, git_revwalk **walk_out, git_reposito
unsigned int i;
if (length < 2) {
- giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %u.", length);
+ giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %" PRIuZ ".", length);
return -1;
}
@@ -185,7 +185,7 @@ int git_merge_base_octopus(git_oid *out, git_repository *repo, size_t length, co
assert(out && repo && input_array);
if (length < 2) {
- giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %u.", length);
+ giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %" PRIuZ ".", length);
return -1;
}
@@ -2451,7 +2451,7 @@ int git_merge__check_result(git_repository *repo, git_index *index_new)
goto done;
if ((conflicts = index_conflicts + wd_conflicts) > 0) {
- giterr_set(GITERR_MERGE, "%d uncommitted change%s would be overwritten by merge",
+ giterr_set(GITERR_MERGE, "%" PRIuZ " uncommitted change%s would be overwritten by merge",
conflicts, (conflicts != 1) ? "s" : "");
error = GIT_ECONFLICT;
}
diff --git a/src/openssl_stream.c b/src/openssl_stream.c
index 958252e9f..8ff53d4b1 100644
--- a/src/openssl_stream.c
+++ b/src/openssl_stream.c
@@ -324,7 +324,9 @@ int openssl_connect(git_stream *stream)
SSL_set_bio(st->ssl, bio, bio);
/* specify the host in case SNI is needed */
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
SSL_set_tlsext_host_name(st->ssl, st->host);
+#endif
if ((ret = SSL_connect(st->ssl)) <= 0)
return ssl_set_error(st->ssl, ret);
@@ -358,11 +360,12 @@ int openssl_certificate(git_cert **out, git_stream *stream)
return -1;
}
- st->cert_info.cert_type = GIT_CERT_X509;
+ st->cert_info.parent.cert_type = GIT_CERT_X509;
st->cert_info.data = encoded_cert;
st->cert_info.len = len;
- *out = (git_cert *)&st->cert_info;
+ *out = &st->cert_info.parent;
+
return 0;
}
diff --git a/src/rebase.c b/src/rebase.c
index 8da7b4f7f..17536c030 100644
--- a/src/rebase.c
+++ b/src/rebase.c
@@ -436,7 +436,7 @@ static int rebase_setupfiles_merge(git_rebase *rebase)
size_t i;
int error = 0;
- if ((error = rebase_setupfile(rebase, END_FILE, -1, "%d\n", git_array_size(rebase->operations))) < 0 ||
+ if ((error = rebase_setupfile(rebase, END_FILE, -1, "%" PRIuZ "\n", git_array_size(rebase->operations))) < 0 ||
(error = rebase_setupfile(rebase, ONTO_NAME_FILE, -1, "%s\n", rebase->onto_name)) < 0)
goto done;
@@ -789,7 +789,7 @@ static int rebase_next_merge(
normalize_checkout_options_for_apply(&checkout_opts, rebase, current_commit);
if ((error = git_indexwriter_init_for_operation(&indexwriter, rebase->repo, &checkout_opts.checkout_strategy)) < 0 ||
- (error = rebase_setupfile(rebase, MSGNUM_FILE, -1, "%d\n", rebase->current+1)) < 0 ||
+ (error = rebase_setupfile(rebase, MSGNUM_FILE, -1, "%" PRIuZ "\n", rebase->current+1)) < 0 ||
(error = rebase_setupfile(rebase, CURRENT_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, current_idstr)) < 0 ||
(error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, NULL)) < 0 ||
(error = git_merge__check_result(rebase->repo, index)) < 0 ||
diff --git a/src/repository.c b/src/repository.c
index 82d998124..08f4baa20 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -867,7 +867,9 @@ static int reserved_names_add8dot3(git_repository *repo, const char *path)
{
char *name = git_win32_path_8dot3_name(path);
const char *def = GIT_DIR_SHORTNAME;
+ const char *def_dot_git = DOT_GIT;
size_t name_len, def_len = CONST_STRLEN(GIT_DIR_SHORTNAME);
+ size_t def_dot_git_len = CONST_STRLEN(DOT_GIT);
git_buf *buf;
if (!name)
@@ -875,7 +877,8 @@ static int reserved_names_add8dot3(git_repository *repo, const char *path)
name_len = strlen(name);
- if (name_len == def_len && memcmp(name, def, def_len) == 0) {
+ if ((name_len == def_len && memcmp(name, def, def_len) == 0) ||
+ (name_len == def_dot_git_len && memcmp(name, def_dot_git, def_dot_git_len) == 0)) {
git__free(name);
return 0;
}
diff --git a/src/stash.c b/src/stash.c
index acf89442a..fcb1112ac 100644
--- a/src/stash.c
+++ b/src/stash.c
@@ -770,7 +770,7 @@ static int ensure_clean_index(git_repository *repo, git_index *index)
goto done;
if (git_diff_num_deltas(index_diff) > 0) {
- giterr_set(GITERR_STASH, "%d uncommitted changes exist in the index",
+ giterr_set(GITERR_STASH, "%" PRIuZ " uncommitted changes exist in the index",
git_diff_num_deltas(index_diff));
error = GIT_EUNCOMMITTED;
}
diff --git a/src/stransport_stream.c b/src/stransport_stream.c
index 10e19166c..33b6c5c38 100644
--- a/src/stransport_stream.c
+++ b/src/stransport_stream.c
@@ -108,7 +108,7 @@ int stransport_certificate(git_cert **out, git_stream *stream)
return -1;
}
- st->cert_info.cert_type = GIT_CERT_X509;
+ st->cert_info.parent.cert_type = GIT_CERT_X509;
st->cert_info.data = (void *) CFDataGetBytePtr(st->der_data);
st->cert_info.len = CFDataGetLength(st->der_data);
diff --git a/src/submodule.c b/src/submodule.c
index 17e1a3561..892c98304 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -1385,7 +1385,7 @@ int git_submodule_reload(git_submodule *sm, int force)
git_buf_sets(&path, "submodule\\.");
git_buf_text_puts_escape_regex(&path, sm->name);
- git_buf_puts(&path, ".*");
+ git_buf_puts(&path, "\\..*");
if (git_buf_oom(&path)) {
error = -1;
@@ -1647,7 +1647,7 @@ static int submodule_load_from_config(
} else {
khiter_t pos;
git_strmap *map = data->map;
- pos = git_strmap_lookup_index(map, name.ptr);
+ pos = git_strmap_lookup_index(map, path ? path : name.ptr);
if (git_strmap_valid_index(map, pos)) {
sm = git_strmap_value_at(map, pos);
} else {
diff --git a/src/thread-utils.c b/src/thread-utils.c
index c3baf411a..dc9b2f09e 100644
--- a/src/thread-utils.c
+++ b/src/thread-utils.c
@@ -8,7 +8,9 @@
#include "thread-utils.h"
#ifdef _WIN32
+#ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
+#endif
# include <windows.h>
#elif defined(hpux) || defined(__hpux) || defined(_hpux)
# include <sys/pstat.h>
diff --git a/src/transports/http.c b/src/transports/http.c
index dd4426475..1ed292be5 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -511,7 +511,7 @@ static int write_chunk(git_stream *io, const char *buffer, size_t len)
git_buf buf = GIT_BUF_INIT;
/* Chunk header */
- git_buf_printf(&buf, "%X\r\n", (unsigned)len);
+ git_buf_printf(&buf, "%" PRIxZ "\r\n", len);
if (git_buf_oom(&buf))
return -1;
diff --git a/src/transports/smart_pkt.c b/src/transports/smart_pkt.c
index d214c9fa5..9ccbd8085 100644
--- a/src/transports/smart_pkt.c
+++ b/src/transports/smart_pkt.c
@@ -523,7 +523,7 @@ static int buffer_want_with_caps(const git_remote_head *head, transport_smart_ca
if (len > 0xffff) {
giterr_set(GITERR_NET,
- "Tried to produce packet with invalid length %d", len);
+ "Tried to produce packet with invalid length %" PRIuZ, len);
return -1;
}
diff --git a/src/transports/ssh.c b/src/transports/ssh.c
index 83af137f8..0b0d4bac3 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -525,10 +525,10 @@ static int _git_ssh_setup_conn(
goto done;
if (t->owner->certificate_check_cb != NULL) {
- git_cert_hostkey cert = { 0 }, *cert_ptr;
+ git_cert_hostkey cert = {{ 0 }}, *cert_ptr;
const char *key;
- cert.cert_type = GIT_CERT_HOSTKEY_LIBSSH2;
+ cert.parent.cert_type = GIT_CERT_HOSTKEY_LIBSSH2;
key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
if (key != NULL) {
diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c
index da047d690..0c43c4b0b 100644
--- a/src/transports/winhttp.c
+++ b/src/transports/winhttp.c
@@ -228,7 +228,7 @@ static int certificate_check(winhttp_stream *s, int valid)
}
giterr_clear();
- cert.cert_type = GIT_CERT_X509;
+ cert.parent.cert_type = GIT_CERT_X509;
cert.data = cert_ctx->pbCertEncoded;
cert.len = cert_ctx->cbCertEncoded;
error = t->owner->certificate_check_cb((git_cert *) &cert, valid, t->connection_data.host, t->owner->cred_acquire_payload);
diff --git a/src/util.h b/src/util.h
index 458b0db41..d0c3cd04a 100644
--- a/src/util.h
+++ b/src/util.h
@@ -38,6 +38,7 @@
*/
#include <stdlib.h>
#include <crtdbg.h>
+#include "win32/w32_crtdbg_stacktrace.h"
#endif
#include "common.h"
@@ -65,23 +66,24 @@
#define CONST_STRLEN(x) ((sizeof(x)/sizeof(x[0])) - 1)
#if defined(GIT_MSVC_CRTDBG)
+
GIT_INLINE(void *) git__crtdbg__malloc(size_t len, const char *file, int line)
{
- void *ptr = _malloc_dbg(len, _NORMAL_BLOCK, file, line);
+ void *ptr = _malloc_dbg(len, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
if (!ptr) giterr_set_oom();
return ptr;
}
GIT_INLINE(void *) git__crtdbg__calloc(size_t nelem, size_t elsize, const char *file, int line)
{
- void *ptr = _calloc_dbg(nelem, elsize, _NORMAL_BLOCK, file, line);
+ void *ptr = _calloc_dbg(nelem, elsize, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
if (!ptr) giterr_set_oom();
return ptr;
}
GIT_INLINE(char *) git__crtdbg__strdup(const char *str, const char *file, int line)
{
- char *ptr = _strdup_dbg(str, _NORMAL_BLOCK, file, line);
+ char *ptr = _strdup_dbg(str, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
if (!ptr) giterr_set_oom();
return ptr;
}
@@ -121,7 +123,7 @@ GIT_INLINE(char *) git__crtdbg__substrdup(const char *start, size_t n, const cha
GIT_INLINE(void *) git__crtdbg__realloc(void *ptr, size_t size, const char *file, int line)
{
- void *new_ptr = _realloc_dbg(ptr, size, _NORMAL_BLOCK, file, line);
+ void *new_ptr = _realloc_dbg(ptr, size, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
if (!new_ptr) giterr_set_oom();
return new_ptr;
}
@@ -129,8 +131,9 @@ GIT_INLINE(void *) git__crtdbg__realloc(void *ptr, size_t size, const char *file
GIT_INLINE(void *) git__crtdbg__reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line)
{
size_t newsize;
+
return GIT_MULTIPLY_SIZET_OVERFLOW(&newsize, nelem, elsize) ?
- NULL : _realloc_dbg(ptr, newsize, _NORMAL_BLOCK, file, line);
+ NULL : _realloc_dbg(ptr, newsize, _NORMAL_BLOCK, git_win32__crtdbg_stacktrace(1,file), line);
}
GIT_INLINE(void *) git__crtdbg__mallocarray(size_t nelem, size_t elsize, const char *file, int line)
diff --git a/src/win32/w32_crtdbg_stacktrace.c b/src/win32/w32_crtdbg_stacktrace.c
new file mode 100644
index 000000000..a778f4164
--- /dev/null
+++ b/src/win32/w32_crtdbg_stacktrace.c
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#if defined(GIT_MSVC_CRTDBG)
+#include "w32_stack.h"
+#include "w32_crtdbg_stacktrace.h"
+
+#define CRTDBG_STACKTRACE__UID_LEN (15)
+
+/**
+ * The stacktrace of an allocation can be distilled
+ * to a unique id based upon the stackframe pointers
+ * and ignoring any size arguments. We will use these
+ * UIDs as the (char const*) __FILE__ argument we
+ * give to the CRT malloc routines.
+ */
+typedef struct {
+ char uid[CRTDBG_STACKTRACE__UID_LEN + 1];
+} git_win32__crtdbg_stacktrace__uid;
+
+/**
+ * All mallocs with the same stacktrace will be de-duped
+ * and aggregated into this row.
+ */
+typedef struct {
+ git_win32__crtdbg_stacktrace__uid uid; /* must be first */
+ git_win32__stack__raw_data raw_data;
+ unsigned int count_allocs; /* times this alloc signature seen since init */
+ unsigned int count_allocs_at_last_checkpoint; /* times since last mark */
+ unsigned int transient_count_leaks; /* sum of leaks */
+} git_win32__crtdbg_stacktrace__row;
+
+static CRITICAL_SECTION g_crtdbg_stacktrace_cs;
+
+/**
+ * CRTDBG memory leak tracking takes a "char const * const file_name"
+ * and stores the pointer in the heap data (instead of allocing a copy
+ * for itself). Normally, this is not a problem, since we usually pass
+ * in __FILE__. But I'm going to lie to it and pass in the address of
+ * the UID in place of the file_name. Also, I do not want to alloc the
+ * stacktrace data (because we are called from inside our alloc routines).
+ * Therefore, I'm creating a very large static pool array to store row
+ * data. This also eliminates the temptation to realloc it (and move the
+ * UID pointers).
+ *
+ * And to efficiently look for duplicates we need an index on the rows
+ * so we can bsearch it. Again, without mallocing.
+ *
+ * If we observe more than MY_ROW_LIMIT unique malloc signatures, we
+ * fall through and use the traditional __FILE__ processing and don't
+ * try to de-dup them. If your testing hits this limit, just increase
+ * it and try again.
+ */
+
+#define MY_ROW_LIMIT (1024 * 1024)
+static git_win32__crtdbg_stacktrace__row g_cs_rows[MY_ROW_LIMIT];
+static git_win32__crtdbg_stacktrace__row *g_cs_index[MY_ROW_LIMIT];
+
+static unsigned int g_cs_end = MY_ROW_LIMIT;
+static unsigned int g_cs_ins = 0; /* insertion point == unique allocs seen */
+static unsigned int g_count_total_allocs = 0; /* number of allocs seen */
+static unsigned int g_transient_count_total_leaks = 0; /* number of total leaks */
+static unsigned int g_transient_count_dedup_leaks = 0; /* number of unique leaks */
+static bool g_limit_reached = false; /* had allocs after we filled row table */
+
+static unsigned int g_checkpoint_id = 0; /* to better label leak checkpoints */
+static bool g_transient_leaks_since_mark = false; /* payload for hook */
+
+/**
+ * Compare function for bsearch on g_cs_index table.
+ */
+static int row_cmp(const void *v1, const void *v2)
+{
+ git_win32__stack__raw_data *d1 = (git_win32__stack__raw_data*)v1;
+ git_win32__crtdbg_stacktrace__row *r2 = (git_win32__crtdbg_stacktrace__row *)v2;
+
+ return (git_win32__stack_compare(d1, &r2->raw_data));
+}
+
+/**
+ * Unique insert the new data into the row and index tables.
+ * We have to sort by the stackframe data itself, not the uid.
+ */
+static git_win32__crtdbg_stacktrace__row * insert_unique(
+ const git_win32__stack__raw_data *pdata)
+{
+ size_t pos;
+ if (git__bsearch(g_cs_index, g_cs_ins, pdata, row_cmp, &pos) < 0) {
+ /* Append new unique item to row table. */
+ memcpy(&g_cs_rows[g_cs_ins].raw_data, pdata, sizeof(*pdata));
+ sprintf(g_cs_rows[g_cs_ins].uid.uid, "##%08lx", g_cs_ins);
+
+ /* Insert pointer to it into the proper place in the index table. */
+ if (pos < g_cs_ins)
+ memmove(&g_cs_index[pos+1], &g_cs_index[pos], (g_cs_ins - pos)*sizeof(g_cs_index[0]));
+ g_cs_index[pos] = &g_cs_rows[g_cs_ins];
+
+ g_cs_ins++;
+ }
+
+ g_cs_index[pos]->count_allocs++;
+
+ return g_cs_index[pos];
+}
+
+/**
+ * Hook function to receive leak data from the CRT. (This includes
+ * both "<file_name>:(<line_number>)" data, but also each of the
+ * various headers and fields.
+ *
+ * Scan this for the special "##<pos>" UID forms that we substituted
+ * for the "<file_name>". Map <pos> back to the row data and
+ * increment its leak count.
+ *
+ * See https://msdn.microsoft.com/en-us/library/74kabxyx.aspx
+ *
+ * We suppress the actual crtdbg output.
+ */
+static int __cdecl report_hook(int nRptType, char *szMsg, int *retVal)
+{
+ static int hook_result = TRUE; /* FALSE to get stock dump; TRUE to suppress. */
+ unsigned int pos;
+
+ *retVal = 0; /* do not invoke debugger */
+
+ if ((szMsg[0] != '#') || (szMsg[1] != '#'))
+ return hook_result;
+
+ if (sscanf(&szMsg[2], "%08lx", &pos) < 1)
+ return hook_result;
+ if (pos >= g_cs_ins)
+ return hook_result;
+
+ if (g_transient_leaks_since_mark) {
+ if (g_cs_rows[pos].count_allocs == g_cs_rows[pos].count_allocs_at_last_checkpoint)
+ return hook_result;
+ }
+
+ g_cs_rows[pos].transient_count_leaks++;
+
+ if (g_cs_rows[pos].transient_count_leaks == 1)
+ g_transient_count_dedup_leaks++;
+
+ g_transient_count_total_leaks++;
+
+ return hook_result;
+}
+
+/**
+ * Write leak data to all of the various places we need.
+ * We force the caller to sprintf() the message first
+ * because we want to avoid fprintf() because it allocs.
+ */
+static void my_output(const char *buf)
+{
+ fwrite(buf, strlen(buf), 1, stderr);
+ OutputDebugString(buf);
+}
+
+/**
+ * For each row with leaks, dump a stacktrace for it.
+ */
+static void dump_summary(const char *label)
+{
+ unsigned int k;
+ char buf[10 * 1024];
+
+ if (g_transient_count_total_leaks == 0)
+ return;
+
+ fflush(stdout);
+ fflush(stderr);
+ my_output("\n");
+
+ if (g_limit_reached) {
+ sprintf(buf,
+ "LEAK SUMMARY: de-dup row table[%d] filled. Increase MY_ROW_LIMIT.\n",
+ MY_ROW_LIMIT);
+ my_output(buf);
+ }
+
+ if (!label)
+ label = "";
+
+ if (g_transient_leaks_since_mark) {
+ sprintf(buf, "LEAK CHECKPOINT %d: leaks %d unique %d: %s\n",
+ g_checkpoint_id, g_transient_count_total_leaks, g_transient_count_dedup_leaks, label);
+ my_output(buf);
+ } else {
+ sprintf(buf, "LEAK SUMMARY: TOTAL leaks %d de-duped %d: %s\n",
+ g_transient_count_total_leaks, g_transient_count_dedup_leaks, label);
+ my_output(buf);
+ }
+ my_output("\n");
+
+ for (k = 0; k < g_cs_ins; k++) {
+ if (g_cs_rows[k].transient_count_leaks > 0) {
+ sprintf(buf, "LEAK: %s leaked %d of %d times:\n",
+ g_cs_rows[k].uid.uid,
+ g_cs_rows[k].transient_count_leaks,
+ g_cs_rows[k].count_allocs);
+ my_output(buf);
+
+ if (git_win32__stack_format(
+ buf, sizeof(buf), &g_cs_rows[k].raw_data,
+ NULL, NULL) >= 0) {
+ my_output(buf);
+ }
+
+ my_output("\n");
+ }
+ }
+
+ fflush(stderr);
+}
+
+void git_win32__crtdbg_stacktrace_init(void)
+{
+ InitializeCriticalSection(&g_crtdbg_stacktrace_cs);
+
+ EnterCriticalSection(&g_crtdbg_stacktrace_cs);
+
+ _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
+
+ _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
+ _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
+ _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
+
+ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
+ _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
+ _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
+
+ LeaveCriticalSection(&g_crtdbg_stacktrace_cs);
+}
+
+int git_win32__crtdbg_stacktrace__dump(
+ git_win32__crtdbg_stacktrace_options opt,
+ const char *label)
+{
+ _CRT_REPORT_HOOK old;
+ unsigned int k;
+ int r = 0;
+
+#define IS_BIT_SET(o,b) (((o) & (b)) != 0)
+
+ bool b_set_mark = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__SET_MARK);
+ bool b_leaks_since_mark = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK);
+ bool b_leaks_total = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_TOTAL);
+ bool b_quiet = IS_BIT_SET(opt, GIT_WIN32__CRTDBG_STACKTRACE__QUIET);
+
+ if (b_leaks_since_mark && b_leaks_total) {
+ giterr_set(GITERR_INVALID, "Cannot combine LEAKS_SINCE_MARK and LEAKS_TOTAL.");
+ return GIT_ERROR;
+ }
+ if (!b_set_mark && !b_leaks_since_mark && !b_leaks_total) {
+ giterr_set(GITERR_INVALID, "Nothing to do.");
+ return GIT_ERROR;
+ }
+
+ EnterCriticalSection(&g_crtdbg_stacktrace_cs);
+
+ if (b_leaks_since_mark || b_leaks_total) {
+ /* All variables with "transient" in the name are per-dump counters
+ * and reset before each dump. This lets us handle checkpoints.
+ */
+ g_transient_count_total_leaks = 0;
+ g_transient_count_dedup_leaks = 0;
+ for (k = 0; k < g_cs_ins; k++) {
+ g_cs_rows[k].transient_count_leaks = 0;
+ }
+ }
+
+ g_transient_leaks_since_mark = b_leaks_since_mark;
+
+ old = _CrtSetReportHook(report_hook);
+ _CrtDumpMemoryLeaks();
+ _CrtSetReportHook(old);
+
+ if (b_leaks_since_mark || b_leaks_total) {
+ r = g_transient_count_dedup_leaks;
+
+ if (!b_quiet)
+ dump_summary(label);
+ }
+
+ if (b_set_mark) {
+ for (k = 0; k < g_cs_ins; k++) {
+ g_cs_rows[k].count_allocs_at_last_checkpoint = g_cs_rows[k].count_allocs;
+ }
+
+ g_checkpoint_id++;
+ }
+
+ LeaveCriticalSection(&g_crtdbg_stacktrace_cs);
+
+ return r;
+}
+
+void git_win32__crtdbg_stacktrace_cleanup(void)
+{
+ /* At shutdown/cleanup, dump cummulative leak info
+ * with everything since startup. This might generate
+ * extra noise if the caller has been doing checkpoint
+ * dumps, but it might also eliminate some false
+ * positives for resources previously reported during
+ * checkpoints.
+ */
+ git_win32__crtdbg_stacktrace__dump(
+ GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_TOTAL,
+ "CLEANUP");
+
+ DeleteCriticalSection(&g_crtdbg_stacktrace_cs);
+}
+
+const char *git_win32__crtdbg_stacktrace(int skip, const char *file)
+{
+ git_win32__stack__raw_data new_data;
+ git_win32__crtdbg_stacktrace__row *row;
+ const char * result = file;
+
+ if (git_win32__stack_capture(&new_data, skip+1) < 0)
+ return result;
+
+ EnterCriticalSection(&g_crtdbg_stacktrace_cs);
+
+ if (g_cs_ins < g_cs_end) {
+ row = insert_unique(&new_data);
+ result = row->uid.uid;
+ } else {
+ g_limit_reached = true;
+ }
+
+ g_count_total_allocs++;
+
+ LeaveCriticalSection(&g_crtdbg_stacktrace_cs);
+
+ return result;
+}
+#endif
diff --git a/src/win32/w32_crtdbg_stacktrace.h b/src/win32/w32_crtdbg_stacktrace.h
new file mode 100644
index 000000000..40ca60d53
--- /dev/null
+++ b/src/win32/w32_crtdbg_stacktrace.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_w32_crtdbg_stacktrace_h__
+#define INCLUDE_w32_crtdbg_stacktrace_h__
+
+#if defined(GIT_MSVC_CRTDBG)
+
+/**
+ * Initialize our memory leak tracking and de-dup data structures.
+ * This should ONLY be called by git_libgit2_init().
+ */
+void git_win32__crtdbg_stacktrace_init(void);
+
+/**
+ * Shutdown our memory leak tracking and dump summary data.
+ * This should ONLY be called by git_libgit2_shutdown().
+ *
+ * We explicitly call _CrtDumpMemoryLeaks() during here so
+ * that we can compute summary data for the leaks. We print
+ * the stacktrace of each unique leak.
+ *
+ * This cleanup does not happen if the app calls exit()
+ * without calling the libgit2 shutdown code.
+ *
+ * This info we print here is independent of any automatic
+ * reporting during exit() caused by _CRTDBG_LEAK_CHECK_DF.
+ * Set it in your app if you also want traditional reporting.
+ */
+void git_win32__crtdbg_stacktrace_cleanup(void);
+
+/**
+ * Checkpoint options.
+ */
+typedef enum git_win32__crtdbg_stacktrace_options {
+ /**
+ * Set checkpoint marker.
+ */
+ GIT_WIN32__CRTDBG_STACKTRACE__SET_MARK = (1 << 0),
+
+ /**
+ * Dump leaks since last checkpoint marker.
+ * May not be combined with __LEAKS_TOTAL.
+ *
+ * Note that this may generate false positives for global TLS
+ * error state and other global caches that aren't cleaned up
+ * until the thread/process terminates. So when using this
+ * around a region of interest, also check the final (at exit)
+ * dump before digging into leaks reported here.
+ */
+ GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK = (1 << 1),
+
+ /**
+ * Dump leaks since init. May not be combined
+ * with __LEAKS_SINCE_MARK.
+ */
+ GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_TOTAL = (1 << 2),
+
+ /**
+ * Suppress printing during dumps.
+ * Just return leak count.
+ */
+ GIT_WIN32__CRTDBG_STACKTRACE__QUIET = (1 << 3),
+
+} git_win32__crtdbg_stacktrace_options;
+
+/**
+ * Checkpoint memory state and/or dump unique stack traces of
+ * current memory leaks.
+ *
+ * @return number of unique leaks (relative to requested starting
+ * point) or error.
+ */
+GIT_EXTERN(int) git_win32__crtdbg_stacktrace__dump(
+ git_win32__crtdbg_stacktrace_options opt,
+ const char *label);
+
+/**
+ * Construct stacktrace and append it to the global buffer.
+ * Return pointer to start of this string. On any error or
+ * lack of buffer space, just return the given file buffer
+ * so it will behave as usual.
+ *
+ * This should ONLY be called by our internal memory allocations
+ * routines.
+ */
+const char *git_win32__crtdbg_stacktrace(int skip, const char *file);
+
+#endif
+#endif
diff --git a/src/win32/w32_stack.c b/src/win32/w32_stack.c
new file mode 100644
index 000000000..15af3dcb7
--- /dev/null
+++ b/src/win32/w32_stack.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#if defined(GIT_MSVC_CRTDBG)
+#include "Windows.h"
+#include "Dbghelp.h"
+#include "win32/posix.h"
+#include "w32_stack.h"
+#include "hash.h"
+
+/**
+ * This is supposedly defined in WinBase.h (from Windows.h) but there were linker issues.
+ */
+USHORT WINAPI RtlCaptureStackBackTrace(ULONG, ULONG, PVOID*, PULONG);
+
+static bool g_win32_stack_initialized = false;
+static HANDLE g_win32_stack_process = INVALID_HANDLE_VALUE;
+static git_win32__stack__aux_cb_alloc g_aux_cb_alloc = NULL;
+static git_win32__stack__aux_cb_lookup g_aux_cb_lookup = NULL;
+
+int git_win32__stack__set_aux_cb(
+ git_win32__stack__aux_cb_alloc cb_alloc,
+ git_win32__stack__aux_cb_lookup cb_lookup)
+{
+ g_aux_cb_alloc = cb_alloc;
+ g_aux_cb_lookup = cb_lookup;
+
+ return 0;
+}
+
+void git_win32__stack_init(void)
+{
+ if (!g_win32_stack_initialized) {
+ g_win32_stack_process = GetCurrentProcess();
+ SymSetOptions(SYMOPT_LOAD_LINES);
+ SymInitialize(g_win32_stack_process, NULL, TRUE);
+ g_win32_stack_initialized = true;
+ }
+}
+
+void git_win32__stack_cleanup(void)
+{
+ if (g_win32_stack_initialized) {
+ SymCleanup(g_win32_stack_process);
+ g_win32_stack_process = INVALID_HANDLE_VALUE;
+ g_win32_stack_initialized = false;
+ }
+}
+
+int git_win32__stack_capture(git_win32__stack__raw_data *pdata, int skip)
+{
+ if (!g_win32_stack_initialized) {
+ giterr_set(GITERR_INVALID, "git_win32_stack not initialized.");
+ return GIT_ERROR;
+ }
+
+ memset(pdata, 0, sizeof(*pdata));
+ pdata->nr_frames = RtlCaptureStackBackTrace(
+ skip+1, GIT_WIN32__STACK__MAX_FRAMES, pdata->frames, NULL);
+
+ /* If an "aux" data provider was registered, ask it to capture
+ * whatever data it needs and give us an "aux_id" to it so that
+ * we can refer to it later when reporting.
+ */
+ if (g_aux_cb_alloc)
+ (g_aux_cb_alloc)(&pdata->aux_id);
+
+ return 0;
+}
+
+int git_win32__stack_compare(
+ git_win32__stack__raw_data *d1,
+ git_win32__stack__raw_data *d2)
+{
+ return memcmp(d1, d2, sizeof(*d1));
+}
+
+int git_win32__stack_format(
+ char *pbuf, int buf_len,
+ const git_win32__stack__raw_data *pdata,
+ const char *prefix, const char *suffix)
+{
+#define MY_MAX_FILENAME 255
+
+ /* SYMBOL_INFO has char FileName[1] at the end. The docs say to
+ * to malloc it with extra space for your desired max filename.
+ */
+ struct {
+ SYMBOL_INFO symbol;
+ char extra[MY_MAX_FILENAME + 1];
+ } s;
+
+ IMAGEHLP_LINE64 line;
+ int buf_used = 0;
+ unsigned int k;
+ char detail[MY_MAX_FILENAME * 2]; /* filename plus space for function name and formatting */
+ int detail_len;
+
+ if (!g_win32_stack_initialized) {
+ giterr_set(GITERR_INVALID, "git_win32_stack not initialized.");
+ return GIT_ERROR;
+ }
+
+ if (!prefix)
+ prefix = "\t";
+ if (!suffix)
+ suffix = "\n";
+
+ memset(pbuf, 0, buf_len);
+
+ memset(&s, 0, sizeof(s));
+ s.symbol.MaxNameLen = MY_MAX_FILENAME;
+ s.symbol.SizeOfStruct = sizeof(SYMBOL_INFO);
+
+ memset(&line, 0, sizeof(line));
+ line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+
+ for (k=0; k < pdata->nr_frames; k++) {
+ DWORD64 frame_k = (DWORD64)pdata->frames[k];
+ DWORD dwUnused;
+
+ if (SymFromAddr(g_win32_stack_process, frame_k, 0, &s.symbol) &&
+ SymGetLineFromAddr64(g_win32_stack_process, frame_k, &dwUnused, &line)) {
+ const char *pslash;
+ const char *pfile;
+
+ pslash = strrchr(line.FileName, '\\');
+ pfile = ((pslash) ? (pslash+1) : line.FileName);
+ p_snprintf(detail, sizeof(detail), "%s%s:%d> %s%s",
+ prefix, pfile, line.LineNumber, s.symbol.Name, suffix);
+ } else {
+ /* This happens when we cross into another module.
+ * For example, in CLAR tests, this is typically
+ * the CRT startup code. Just print an unknown
+ * frame and continue.
+ */
+ p_snprintf(detail, sizeof(detail), "%s??%s", prefix, suffix);
+ }
+ detail_len = strlen(detail);
+
+ if (buf_len < (buf_used + detail_len + 1)) {
+ /* we don't have room for this frame in the buffer, so just stop. */
+ break;
+ }
+
+ memcpy(&pbuf[buf_used], detail, detail_len);
+ buf_used += detail_len;
+ }
+
+ /* "aux_id" 0 is reserved to mean no aux data. This is needed to handle
+ * allocs that occur before the aux callbacks were registered.
+ */
+ if (pdata->aux_id > 0) {
+ p_snprintf(detail, sizeof(detail), "%saux_id: %d%s",
+ prefix, pdata->aux_id, suffix);
+ detail_len = strlen(detail);
+ if ((buf_used + detail_len + 1) < buf_len) {
+ memcpy(&pbuf[buf_used], detail, detail_len);
+ buf_used += detail_len;
+ }
+
+ /* If an "aux" data provider is still registered, ask it to append its detailed
+ * data to the end of ours using the "aux_id" it gave us when this de-duped
+ * item was created.
+ */
+ if (g_aux_cb_lookup)
+ (g_aux_cb_lookup)(pdata->aux_id, &pbuf[buf_used], (buf_len - buf_used - 1));
+ }
+
+ return GIT_OK;
+}
+
+int git_win32__stack(
+ char * pbuf, int buf_len,
+ int skip,
+ const char *prefix, const char *suffix)
+{
+ git_win32__stack__raw_data data;
+ int error;
+
+ if ((error = git_win32__stack_capture(&data, skip)) < 0)
+ return error;
+ if ((error = git_win32__stack_format(pbuf, buf_len, &data, prefix, suffix)) < 0)
+ return error;
+ return 0;
+}
+
+#endif
diff --git a/src/win32/w32_stack.h b/src/win32/w32_stack.h
new file mode 100644
index 000000000..21170bd2f
--- /dev/null
+++ b/src/win32/w32_stack.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_w32_stack_h__
+#define INCLUDE_w32_stack_h__
+
+#if defined(GIT_MSVC_CRTDBG)
+
+/**
+ * This type defines a callback to be used to augment a C stacktrace
+ * with "aux" data. This can be used, for example, to allow LibGit2Sharp
+ * (or other interpreted consumer libraries) to give us C# stacktrace
+ * data for the PInvoke.
+ *
+ * This callback will be called during crtdbg-instrumented allocs.
+ *
+ * @param aux_id [out] A returned "aux_id" representing a unique
+ * (de-duped at the C# layer) stacktrace. "aux_id" 0 is reserved
+ * to mean no aux stacktrace data.
+ */
+typedef void (*git_win32__stack__aux_cb_alloc)(unsigned int *aux_id);
+
+/**
+ * This type defines a callback to be used to augment the output of
+ * a stacktrace. This will be used to request the C# layer format
+ * the C# stacktrace associated with "aux_id" into the provided
+ * buffer.
+ *
+ * This callback will be called during leak reporting.
+ *
+ * @param aux_id The "aux_id" key associated with a stacktrace.
+ * @param aux_msg A buffer where a formatted message should be written.
+ * @param aux_msg_len The size of the buffer.
+ */
+typedef void (*git_win32__stack__aux_cb_lookup)(unsigned int aux_id, char *aux_msg, unsigned int aux_msg_len);
+
+/**
+ * Register an "aux" data provider to augment our C stacktrace data.
+ *
+ * This can be used, for example, to allow LibGit2Sharp (or other
+ * interpreted consumer libraries) to give us the C# stacktrace of
+ * the PInvoke.
+ *
+ * If you choose to use this feature, it should be registered during
+ * initialization and not changed for the duration of the process.
+ */
+GIT_EXTERN(int) git_win32__stack__set_aux_cb(
+ git_win32__stack__aux_cb_alloc cb_alloc,
+ git_win32__stack__aux_cb_lookup cb_lookup);
+
+/**
+ * Maximum number of stackframes to record for a
+ * single stacktrace.
+ */
+#define GIT_WIN32__STACK__MAX_FRAMES 30
+
+/**
+ * Wrapper containing the raw unprocessed stackframe
+ * data for a single stacktrace and any "aux_id".
+ *
+ * I put the aux_id first so leaks will be sorted by it.
+ * So, for example, if a specific callstack in C# leaks
+ * a repo handle, all of the pointers within the associated
+ * repo pointer will be grouped together.
+ */
+typedef struct {
+ unsigned int aux_id;
+ unsigned int nr_frames;
+ void *frames[GIT_WIN32__STACK__MAX_FRAMES];
+} git_win32__stack__raw_data;
+
+
+/**
+ * Load symbol table data. This should be done in the primary
+ * thread at startup (under a lock if there are other threads
+ * active).
+ */
+void git_win32__stack_init(void);
+
+/**
+ * Cleanup symbol table data. This should be done in the
+ * primary thead at shutdown (under a lock if there are other
+ * threads active).
+ */
+void git_win32__stack_cleanup(void);
+
+
+/**
+ * Capture raw stack trace data for the current process/thread.
+ *
+ * @param skip Number of initial frames to skip. Pass 0 to
+ * begin with the caller of this routine. Pass 1 to begin
+ * with its caller. And so on.
+ */
+int git_win32__stack_capture(git_win32__stack__raw_data *pdata, int skip);
+
+/**
+ * Compare 2 raw stacktraces with the usual -1,0,+1 result.
+ * This includes any "aux_id" values in the comparison, so that
+ * our de-dup is also "aux" context relative.
+ */
+int git_win32__stack_compare(
+ git_win32__stack__raw_data *d1,
+ git_win32__stack__raw_data *d2);
+
+/**
+ * Format raw stacktrace data into buffer WITHOUT using any mallocs.
+ *
+ * @param prefix String written before each frame; defaults to "\t".
+ * @param suffix String written after each frame; defaults to "\n".
+ */
+int git_win32__stack_format(
+ char *pbuf, int buf_len,
+ const git_win32__stack__raw_data *pdata,
+ const char *prefix, const char *suffix);
+
+/**
+ * Convenience routine to capture and format stacktrace into
+ * a buffer WITHOUT using any mallocs. This is primarily a
+ * wrapper for testing.
+ *
+ * @param skip Number of initial frames to skip. Pass 0 to
+ * begin with the caller of this routine. Pass 1 to begin
+ * with its caller. And so on.
+ * @param prefix String written before each frame; defaults to "\t".
+ * @param suffix String written after each frame; defaults to "\n".
+ */
+int git_win32__stack(
+ char * pbuf, int buf_len,
+ int skip,
+ const char *prefix, const char *suffix);
+
+#endif /* GIT_MSVC_CRTDBG */
+#endif /* INCLUDE_w32_stack_h__ */
diff --git a/src/xdiff/xdiff.h b/src/xdiff/xdiff.h
index cb8b235b5..e2f1e892b 100644
--- a/src/xdiff/xdiff.h
+++ b/src/xdiff/xdiff.h
@@ -32,14 +32,14 @@ extern "C" {
#define XDF_IGNORE_WHITESPACE (1 << 2)
#define XDF_IGNORE_WHITESPACE_CHANGE (1 << 3)
#define XDF_IGNORE_WHITESPACE_AT_EOL (1 << 4)
+#define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | XDF_IGNORE_WHITESPACE_CHANGE | XDF_IGNORE_WHITESPACE_AT_EOL)
+
#define XDF_PATIENCE_DIFF (1 << 5)
#define XDF_HISTOGRAM_DIFF (1 << 6)
-#define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | XDF_IGNORE_WHITESPACE_CHANGE | XDF_IGNORE_WHITESPACE_AT_EOL)
+#define XDF_DIFF_ALGORITHM_MASK (XDF_PATIENCE_DIFF | XDF_HISTOGRAM_DIFF)
+#define XDF_DIFF_ALG(x) ((x) & XDF_DIFF_ALGORITHM_MASK)
-#define XDL_PATCH_NORMAL '-'
-#define XDL_PATCH_REVERSE '+'
-#define XDL_PATCH_MODEMASK ((1 << 8) - 1)
-#define XDL_PATCH_IGNOREBSPACE (1 << 8)
+#define XDF_IGNORE_BLANK_LINES (1 << 7)
#define XDL_EMIT_FUNCNAMES (1 << 0)
#define XDL_EMIT_COMMON (1 << 1)
@@ -88,13 +88,17 @@ typedef struct s_xdemitcb {
typedef long (*find_func_t)(const char *line, long line_len, char *buffer, long buffer_size, void *priv);
+typedef int (*xdl_emit_hunk_consume_func_t)(long start_a, long count_a,
+ long start_b, long count_b,
+ void *cb_data);
+
typedef struct s_xdemitconf {
long ctxlen;
long interhunkctxlen;
unsigned long flags;
find_func_t find_func;
void *find_func_priv;
- void (*emit_func)(void);
+ xdl_emit_hunk_consume_func_t hunk_func;
} xdemitconf_t;
typedef struct s_bdiffparam {
diff --git a/src/xdiff/xdiffi.c b/src/xdiff/xdiffi.c
index 84aa0fcfe..0620e5fff 100644
--- a/src/xdiff/xdiffi.c
+++ b/src/xdiff/xdiffi.c
@@ -328,10 +328,10 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
xdalgoenv_t xenv;
diffdata_t dd1, dd2;
- if (xpp->flags & XDF_PATIENCE_DIFF)
+ if (XDF_DIFF_ALG(xpp->flags) == XDF_PATIENCE_DIFF)
return xdl_do_patience_diff(mf1, mf2, xpp, xe);
- if (xpp->flags & XDF_HISTOGRAM_DIFF)
+ if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF)
return xdl_do_histogram_diff(mf1, mf2, xpp, xe);
if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0) {
@@ -394,6 +394,7 @@ static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1,
xch->i2 = i2;
xch->chg1 = chg1;
xch->chg2 = chg2;
+ xch->ignore = 0;
return xch;
}
@@ -538,13 +539,51 @@ void xdl_free_script(xdchange_t *xscr) {
}
}
+static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+ xdemitconf_t const *xecfg)
+{
+ xdchange_t *xch, *xche;
+
+ (void)xe;
+
+ for (xch = xscr; xch; xch = xche->next) {
+ xche = xdl_get_hunk(&xch, xecfg);
+ if (!xch)
+ break;
+ if (xecfg->hunk_func(xch->i1, xche->i1 + xche->chg1 - xch->i1,
+ xch->i2, xche->i2 + xche->chg2 - xch->i2,
+ ecb->priv) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+static void xdl_mark_ignorable(xdchange_t *xscr, xdfenv_t *xe, long flags)
+{
+ xdchange_t *xch;
+
+ for (xch = xscr; xch; xch = xch->next) {
+ int ignore = 1;
+ xrecord_t **rec;
+ long i;
+
+ rec = &xe->xdf1.recs[xch->i1];
+ for (i = 0; i < xch->chg1 && ignore; i++)
+ ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);
+
+ rec = &xe->xdf2.recs[xch->i2];
+ for (i = 0; i < xch->chg2 && ignore; i++)
+ ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);
+
+ xch->ignore = ignore;
+ }
+}
int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
xdemitconf_t const *xecfg, xdemitcb_t *ecb) {
xdchange_t *xscr;
xdfenv_t xe;
- emit_func_t ef = xecfg->emit_func ?
- (emit_func_t)xecfg->emit_func : xdl_emit_diff;
+ emit_func_t ef = xecfg->hunk_func ? xdl_call_hunk_func : xdl_emit_diff;
if (xdl_do_diff(mf1, mf2, xpp, &xe) < 0) {
@@ -558,6 +597,9 @@ int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
return -1;
}
if (xscr) {
+ if (xpp->flags & XDF_IGNORE_BLANK_LINES)
+ xdl_mark_ignorable(xscr, &xe, xpp->flags);
+
if (ef(&xe, xscr, ecb, xecfg) < 0) {
xdl_free_script(xscr);
diff --git a/src/xdiff/xdiffi.h b/src/xdiff/xdiffi.h
index 7a92ea9c4..8b81206c9 100644
--- a/src/xdiff/xdiffi.h
+++ b/src/xdiff/xdiffi.h
@@ -41,6 +41,7 @@ typedef struct s_xdchange {
struct s_xdchange *next;
long i1, i2;
long chg1, chg2;
+ int ignore;
} xdchange_t;
diff --git a/src/xdiff/xemit.c b/src/xdiff/xemit.c
index e3e63d902..600fd1fdd 100644
--- a/src/xdiff/xemit.c
+++ b/src/xdiff/xemit.c
@@ -56,16 +56,51 @@ static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *
/*
* Starting at the passed change atom, find the latest change atom to be included
* inside the differential hunk according to the specified configuration.
+ * Also advance xscr if the first changes must be discarded.
*/
-xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg) {
- xdchange_t *xch, *xchp;
+xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg)
+{
+ xdchange_t *xch, *xchp, *lxch;
long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;
+ long max_ignorable = xecfg->ctxlen;
+ unsigned long ignored = 0; /* number of ignored blank lines */
+
+ /* remove ignorable changes that are too far before other changes */
+ for (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) {
+ xch = xchp->next;
+
+ if (xch == NULL ||
+ xch->i1 - (xchp->i1 + xchp->chg1) >= max_ignorable)
+ *xscr = xch;
+ }
+
+ if (*xscr == NULL)
+ return NULL;
+
+ lxch = *xscr;
- for (xchp = xscr, xch = xscr->next; xch; xchp = xch, xch = xch->next)
- if (xch->i1 - (xchp->i1 + xchp->chg1) > max_common)
+ for (xchp = *xscr, xch = xchp->next; xch; xchp = xch, xch = xch->next) {
+ long distance = xch->i1 - (xchp->i1 + xchp->chg1);
+ if (distance > max_common)
break;
- return xchp;
+ if (distance < max_ignorable && (!xch->ignore || lxch == xchp)) {
+ lxch = xch;
+ ignored = 0;
+ } else if (distance < max_ignorable && xch->ignore) {
+ ignored += xch->chg2;
+ } else if (lxch != xchp &&
+ xch->i1 + ignored - (lxch->i1 + lxch->chg1) > (unsigned long)max_common) {
+ break;
+ } else if (!xch->ignore) {
+ lxch = xch;
+ ignored = 0;
+ } else {
+ ignored += xch->chg2;
+ }
+ }
+
+ return lxch;
}
@@ -144,7 +179,9 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
return xdl_emit_common(xe, xscr, ecb, xecfg);
for (xch = xscr; xch; xch = xche->next) {
- xche = xdl_get_hunk(xch, xecfg);
+ xche = xdl_get_hunk(&xch, xecfg);
+ if (!xch)
+ break;
s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
diff --git a/src/xdiff/xemit.h b/src/xdiff/xemit.h
index c2e2e8302..d29710770 100644
--- a/src/xdiff/xemit.h
+++ b/src/xdiff/xemit.h
@@ -27,7 +27,7 @@
typedef int (*emit_func_t)(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
xdemitconf_t const *xecfg);
-xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg);
+xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg);
int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
xdemitconf_t const *xecfg);
diff --git a/src/xdiff/xhistogram.c b/src/xdiff/xhistogram.c
index c84812893..500d8112a 100644
--- a/src/xdiff/xhistogram.c
+++ b/src/xdiff/xhistogram.c
@@ -258,7 +258,7 @@ static int fall_back_to_classic_diff(struct histindex *index,
int line1, int count1, int line2, int count2)
{
xpparam_t xpp;
- xpp.flags = index->xpp->flags & ~XDF_HISTOGRAM_DIFF;
+ xpp.flags = index->xpp->flags & ~XDF_DIFF_ALGORITHM_MASK;
return xdl_fall_back_diff(index->env, &xpp,
line1, count1, line2, count2);
diff --git a/src/xdiff/xmerge.c b/src/xdiff/xmerge.c
index 84e424672..b11e59817 100644
--- a/src/xdiff/xmerge.c
+++ b/src/xdiff/xmerge.c
@@ -245,11 +245,11 @@ static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
dest ? dest + size : NULL);
/* Postimage from side #1 */
if (m->mode & 1)
- size += xdl_recs_copy(xe1, m->i1, m->chg1, 1,
+ size += xdl_recs_copy(xe1, m->i1, m->chg1, (m->mode & 2),
dest ? dest + size : NULL);
/* Postimage from side #2 */
if (m->mode & 2)
- size += xdl_recs_copy(xe2, m->i2, m->chg2, 1,
+ size += xdl_recs_copy(xe2, m->i2, m->chg2, 0,
dest ? dest + size : NULL);
} else
continue;
diff --git a/src/xdiff/xpatience.c b/src/xdiff/xpatience.c
index fdd7d0263..04e1a1ab2 100644
--- a/src/xdiff/xpatience.c
+++ b/src/xdiff/xpatience.c
@@ -288,7 +288,7 @@ static int fall_back_to_classic_diff(struct hashmap *map,
int line1, int count1, int line2, int count2)
{
xpparam_t xpp;
- xpp.flags = map->xpp->flags & ~XDF_PATIENCE_DIFF;
+ xpp.flags = map->xpp->flags & ~XDF_DIFF_ALGORITHM_MASK;
return xdl_fall_back_diff(map->env, &xpp,
line1, count1, line2, count2);
diff --git a/src/xdiff/xprepare.c b/src/xdiff/xprepare.c
index e419f4f72..63a22c630 100644
--- a/src/xdiff/xprepare.c
+++ b/src/xdiff/xprepare.c
@@ -181,7 +181,7 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_
if (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *))))
goto abort;
- if (xpp->flags & XDF_HISTOGRAM_DIFF)
+ if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF)
hbits = hsize = 0;
else {
hbits = xdl_hashbits((unsigned int) narec);
@@ -209,8 +209,8 @@ static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_
crec->ha = hav;
recs[nrec++] = crec;
- if (!(xpp->flags & XDF_HISTOGRAM_DIFF) &&
- xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)
+ if ((XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&
+ xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)
goto abort;
}
}
@@ -273,16 +273,15 @@ int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
* (nrecs) will be updated correctly anyway by
* xdl_prepare_ctx().
*/
- sample = xpp->flags & XDF_HISTOGRAM_DIFF ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1;
+ sample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF
+ ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);
enl1 = xdl_guess_lines(mf1, sample) + 1;
enl2 = xdl_guess_lines(mf2, sample) + 1;
- if (!(xpp->flags & XDF_HISTOGRAM_DIFF) &&
- xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0) {
-
+ if (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF &&
+ xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)
return -1;
- }
if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {
@@ -296,9 +295,9 @@ int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
return -1;
}
- if (!(xpp->flags & XDF_PATIENCE_DIFF) &&
- !(xpp->flags & XDF_HISTOGRAM_DIFF) &&
- xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {
+ if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&
+ (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&
+ xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {
xdl_free_ctx(&xe->xdf2);
xdl_free_ctx(&xe->xdf1);
diff --git a/src/xdiff/xutils.c b/src/xdiff/xutils.c
index bb7bdee49..30f2a30ac 100644
--- a/src/xdiff/xutils.c
+++ b/src/xdiff/xutils.c
@@ -120,35 +120,6 @@ void *xdl_cha_alloc(chastore_t *cha) {
return data;
}
-
-void *xdl_cha_first(chastore_t *cha) {
- chanode_t *sncur;
-
- if (!(cha->sncur = sncur = cha->head))
- return NULL;
-
- cha->scurr = 0;
-
- return (char *) sncur + sizeof(chanode_t) + cha->scurr;
-}
-
-
-void *xdl_cha_next(chastore_t *cha) {
- chanode_t *sncur;
-
- if (!(sncur = cha->sncur))
- return NULL;
- cha->scurr += cha->isize;
- if (cha->scurr == sncur->icurr) {
- if (!(sncur = cha->sncur = sncur->next))
- return NULL;
- cha->scurr = 0;
- }
-
- return (char *) sncur + sizeof(chanode_t) + cha->scurr;
-}
-
-
long xdl_guess_lines(mmfile_t *mf, long sample) {
long nl = 0, size, tsize = 0;
char const *data, *cur, *top;
@@ -170,6 +141,19 @@ long xdl_guess_lines(mmfile_t *mf, long sample) {
return nl + 1;
}
+int xdl_blankline(const char *line, long size, long flags)
+{
+ long i;
+
+ if (!(flags & XDF_WHITESPACE_FLAGS))
+ return (size <= 1);
+
+ for (i = 0; i < size && XDL_ISSPACE(line[i]); i++)
+ ;
+
+ return (i == size);
+}
+
int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
{
int i1, i2;
diff --git a/src/xdiff/xutils.h b/src/xdiff/xutils.h
index 714719a89..8f952a8e6 100644
--- a/src/xdiff/xutils.h
+++ b/src/xdiff/xutils.h
@@ -34,6 +34,7 @@ void *xdl_cha_alloc(chastore_t *cha);
void *xdl_cha_first(chastore_t *cha);
void *xdl_cha_next(chastore_t *cha);
long xdl_guess_lines(mmfile_t *mf, long sample);
+int xdl_blankline(const char *line, long size, long flags);
int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags);
unsigned long xdl_hash_record(char const **data, char const *top, long flags);
unsigned int xdl_hashbits(unsigned int size);