stdalloc: extend allocators by file and line Our desired architecture would make allocators completely pluggable, such that users of libgit2 can swap out memory allocators at runtime. While making e.g. debugging easier by not having to do a separate build, this feature can also help maintainers of bindings for libgit2 by tying the memory allocations into the other language's memory system. In order to do so, though, we first need to make our two different pre-existing allocators "stdalloc" and "crtdbg" have the same function signatures, as the "crtdbg" allocators all have an additional file and line argument. This is required to build correct stack traces for debugging memory allocations. As that feature may also be interesting to authors of other applications for debugging libgit2, we now simply add these arguments to our standard allocators. Obviously, this may come with a performance penalty. During some simple benchmarks no real impact could be measured though in contrast to a simple pluggable allocator. The following table summarizes the benchmarks. There were three different builds with our current standard allocator ("standard"), with pluggable authenticators accessed via function pointers ("pluggable") and for pluggable authenticators with file and line being added ("fileline"). Furthermore, there were three scenarios for 100.000.000 allocations of 100B ("small alloc"), 100.000.000 allocations of 100KB ("medium alloc"), and 1.000.000 allocations of 100MB. All results are best of 10 runs. |------------|-------------------|-------------------|-------------------| | build/test | small alloc | medium alloc | big alloc | |------------|-------------------|-------------------|-------------------| | standard | 4539779566, +0.0% | 5912927186, +0.0% | 5166935308, +0.0% | |------------|-------------------|-------------------|-------------------| | pluggable | 4611074505, +1.5% | 5979185308, +1.1% | 5388776352, +4.2% | |------------|-------------------|-------------------|-------------------| | fileline | 4588338192, +1.1% | 6004951910, +1.5% | 4942528135, -4.4% | |------------|-------------------|-------------------|-------------------| As can be seen, there is a performance overhead for pluggable allocators. Furthermore, it can also be seen that there is some big variance between runs, especially in the "big alloc" scenario. This is probably being caused by nondeterministic behaviour in the kernel for dynamic allocations. Still, it can be observed that there should be no real difference between the "pluggable" and "fileline" allocators.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
diff --git a/src/alloc.h b/src/alloc.h
index e2dfdbc..486f1e0 100644
--- a/src/alloc.h
+++ b/src/alloc.h
@@ -54,14 +54,14 @@
#include "stdalloc.h"
-#define git__malloc(len) git__stdalloc__malloc(len)
-#define git__calloc(nelem, elsize) git__stdalloc__calloc(nelem, elsize)
-#define git__strdup(str) git__stdalloc__strdup(str)
-#define git__strndup(str, n) git__stdalloc__strndup(str, n)
-#define git__substrdup(str, n) git__stdalloc__substrdup(str, n)
-#define git__realloc(ptr, size) git__stdalloc__realloc(ptr, size)
-#define git__reallocarray(ptr, nelem, elsize) git__stdalloc__reallocarray(ptr, nelem, elsize)
-#define git__mallocarray(nelem, elsize) git__stdalloc__mallocarray(nelem, elsize)
+#define git__malloc(len) git__stdalloc__malloc(len, __FILE__, __LINE__)
+#define git__calloc(nelem, elsize) git__stdalloc__calloc(nelem, elsize, __FILE__, __LINE__)
+#define git__strdup(str) git__stdalloc__strdup(str, __FILE__, __LINE__)
+#define git__strndup(str, n) git__stdalloc__strndup(str, n, __FILE__, __LINE__)
+#define git__substrdup(str, n) git__stdalloc__substrdup(str, n, __FILE__, __LINE__)
+#define git__realloc(ptr, size) git__stdalloc__realloc(ptr, size, __FILE__, __LINE__)
+#define git__reallocarray(ptr, nelem, elsize) git__stdalloc__reallocarray(ptr, nelem, elsize, __FILE__, __LINE__)
+#define git__mallocarray(nelem, elsize) git__stdalloc__mallocarray(nelem, elsize, __FILE__, __LINE__)
#define git__free git__stdalloc__free
#endif /* !MSVC_CTRDBG */
diff --git a/src/stdalloc.c b/src/stdalloc.c
index 1735be7..8bc8b63 100644
--- a/src/stdalloc.c
+++ b/src/stdalloc.c
@@ -7,28 +7,40 @@
#include "stdalloc.h"
-void *git__stdalloc__malloc(size_t len)
+void *git__stdalloc__malloc(size_t len, const char *file, int line)
{
void *ptr = malloc(len);
+
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+
if (!ptr) giterr_set_oom();
return ptr;
}
-void *git__stdalloc__calloc(size_t nelem, size_t elsize)
+void *git__stdalloc__calloc(size_t nelem, size_t elsize, const char *file, int line)
{
void *ptr = calloc(nelem, elsize);
+
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+
if (!ptr) giterr_set_oom();
return ptr;
}
-char *git__stdalloc__strdup(const char *str)
+char *git__stdalloc__strdup(const char *str, const char *file, int line)
{
char *ptr = strdup(str);
+
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+
if (!ptr) giterr_set_oom();
return ptr;
}
-char *git__stdalloc__strndup(const char *str, size_t n)
+char *git__stdalloc__strndup(const char *str, size_t n, const char *file, int line)
{
size_t length = 0, alloclength;
char *ptr;
@@ -36,7 +48,7 @@ char *git__stdalloc__strndup(const char *str, size_t n)
length = p_strnlen(str, n);
if (GIT_ADD_SIZET_OVERFLOW(&alloclength, length, 1) ||
- !(ptr = git__stdalloc__malloc(alloclength)))
+ !(ptr = git__stdalloc__malloc(alloclength, file, line)))
return NULL;
if (length)
@@ -47,13 +59,13 @@ char *git__stdalloc__strndup(const char *str, size_t n)
return ptr;
}
-char *git__stdalloc__substrdup(const char *start, size_t n)
+char *git__stdalloc__substrdup(const char *start, size_t n, const char *file, int line)
{
char *ptr;
size_t alloclen;
if (GIT_ADD_SIZET_OVERFLOW(&alloclen, n, 1) ||
- !(ptr = git__stdalloc__malloc(alloclen)))
+ !(ptr = git__stdalloc__malloc(alloclen, file, line)))
return NULL;
memcpy(ptr, start, n);
@@ -61,23 +73,31 @@ char *git__stdalloc__substrdup(const char *start, size_t n)
return ptr;
}
-void *git__stdalloc__realloc(void *ptr, size_t size)
+void *git__stdalloc__realloc(void *ptr, size_t size, const char *file, int line)
{
void *new_ptr = realloc(ptr, size);
+
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+
if (!new_ptr) giterr_set_oom();
return new_ptr;
}
-void *git__stdalloc__reallocarray(void *ptr, size_t nelem, size_t elsize)
+void *git__stdalloc__reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line)
{
size_t newsize;
+
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+
return GIT_MULTIPLY_SIZET_OVERFLOW(&newsize, nelem, elsize) ?
NULL : realloc(ptr, newsize);
}
-void *git__stdalloc__mallocarray(size_t nelem, size_t elsize)
+void *git__stdalloc__mallocarray(size_t nelem, size_t elsize, const char *file, int line)
{
- return git__stdalloc__reallocarray(NULL, nelem, elsize);
+ return git__stdalloc__reallocarray(NULL, nelem, elsize, file, line);
}
void git__stdalloc__free(void *ptr)
diff --git a/src/stdalloc.h b/src/stdalloc.h
index 17c2072..952a800 100644
--- a/src/stdalloc.h
+++ b/src/stdalloc.h
@@ -15,25 +15,25 @@
* that set error code and error message
* on allocation failure
*/
-void *git__stdalloc__malloc(size_t len);
-void *git__stdalloc__calloc(size_t nelem, size_t elsize);
-char *git__stdalloc__strdup(const char *str);
-char *git__stdalloc__strndup(const char *str, size_t n);
+void *git__stdalloc__malloc(size_t len, const char *file, int line);
+void *git__stdalloc__calloc(size_t nelem, size_t elsize, const char *file, int line);
+char *git__stdalloc__strdup(const char *str, const char *file, int line);
+char *git__stdalloc__strndup(const char *str, size_t n, const char *file, int line);
/* NOTE: This doesn't do null or '\0' checking. Watch those boundaries! */
-char *git__stdalloc__substrdup(const char *start, size_t n);
-void *git__stdalloc__realloc(void *ptr, size_t size);
+char *git__stdalloc__substrdup(const char *start, size_t n, const char *file, int line);
+void *git__stdalloc__realloc(void *ptr, size_t size, const char *file, int line);
/**
* Similar to `git__stdalloc__realloc`, except that it is suitable for reallocing an
* array to a new number of elements of `nelem`, each of size `elsize`.
* The total size calculation is checked for overflow.
*/
-void *git__stdalloc__reallocarray(void *ptr, size_t nelem, size_t elsize);
+void *git__stdalloc__reallocarray(void *ptr, size_t nelem, size_t elsize, const char *file, int line);
/**
* Similar to `git__stdalloc__calloc`, except that it does not zero memory.
*/
-void *git__stdalloc__mallocarray(size_t nelem, size_t elsize);
+void *git__stdalloc__mallocarray(size_t nelem, size_t elsize, const char *file, int line);
void git__stdalloc__free(void *ptr);