diff --git a/changelog/collect-stats.dd b/changelog/collect-stats.dd new file mode 100644 index 0000000000..26c14e76b2 --- /dev/null +++ b/changelog/collect-stats.dd @@ -0,0 +1,5 @@ +`GC.Stats` provide new metric, total collected memory. + +Result struct of `core.memory.GC.stats()` method now provides additional field, +`totalCollected`, which is set to total amount of bytes reclaimed by GC during +collection cycles since the program start. diff --git a/src/core/internal/traits.d b/src/core/internal/traits.d index 8142f90c51..7b1b7bdf69 100644 --- a/src/core/internal/traits.d +++ b/src/core/internal/traits.d @@ -187,3 +187,4 @@ template hasElaborateCopyConstructor(T...) else enum bool hasElaborateCopyConstructor = false; } + diff --git a/src/core/memory.d b/src/core/memory.d index 573ed2e8b2..5f73a32fcd 100644 --- a/src/core/memory.d +++ b/src/core/memory.d @@ -165,9 +165,12 @@ struct GC static struct Stats { /// number of used bytes on the GC heap (might only get updated after a collection) - size_t usedSize; + ulong usedSize; /// number of free bytes on the GC heap (might only get updated after a collection) - size_t freeSize; + ulong freeSize; + /// number of bytes freed during collections through program lifetime so + /// far (will count same memory multiple times if re-used) + ulong totalCollected; } /** diff --git a/src/gc/gcinterface.d b/src/gc/gcinterface.d index abe88f1c82..ad9449e1fe 100644 --- a/src/gc/gcinterface.d +++ b/src/gc/gcinterface.d @@ -17,6 +17,8 @@ static import core.memory; alias BlkAttr = core.memory.GC.BlkAttr; alias BlkInfo = core.memory.GC.BlkInfo; +static import core.sync.mutex; + alias RootIterator = int delegate(scope int delegate(ref Root) nothrow dg); alias RangeIterator = int delegate(scope int delegate(ref Range) nothrow dg); @@ -187,4 +189,7 @@ interface GC * */ bool inFinalizer() nothrow; + + void enableProfiling() nothrow @nogc; + shared(core.sync.mutex.Mutex) profilerLock() nothrow @nogc; } diff --git a/src/gc/impl/conservative/gc.d b/src/gc/impl/conservative/gc.d index fe1ca5f858..bffbf76e38 100644 --- a/src/gc/impl/conservative/gc.d +++ b/src/gc/impl/conservative/gc.d @@ -49,6 +49,7 @@ import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc; import core.stdc.string : memcpy, memset, memmove; import core.bitop; import core.thread; +import core.sync.mutex; static import core.memory; version (GNU) import gcc.builtins; @@ -115,6 +116,7 @@ __gshared long numFrees; __gshared long numReallocs; __gshared long numExtends; __gshared long numOthers; +__gshared long totalCollectedPages; __gshared long mallocTime; // using ticks instead of MonoTime for better performance __gshared long freeTime; __gshared long reallocTime; @@ -251,6 +253,10 @@ debug (LOGGING) class ConservativeGC : GC { + bool profiling_enabled; + shared Mutex profiler_lock; + shared void[__traits(classInstanceSize, Mutex)] profiler_lock_bytes; + // For passing to debug code (not thread safe) __gshared size_t line; __gshared char* file; @@ -315,6 +321,10 @@ class ConservativeGC : GC gcx.reserve(config.initReserve << 20); if (config.disable) gcx.disabled++; + + this.profiler_lock_bytes[0..$] = typeid(Mutex).initializer[]; + this.profiler_lock = cast(shared(Mutex)) this.profiler_lock_bytes.ptr; + this.profiler_lock.__ctor(); } @@ -358,6 +368,13 @@ class ConservativeGC : GC auto runLocked(alias func, Args...)(auto ref Args args) { + if (this.profiling_enabled) + { + this.profiler_lock.lock_nothrow(); + scope(exit) + this.profiler_lock.unlock_nothrow(); + } + debug(PROFILE_API) immutable tm = (config.profile > 1 ? currTime.ticks : 0); lockNR(); scope (failure) gcLock.unlock(); @@ -1227,6 +1244,17 @@ class ConservativeGC : GC stats.usedSize -= freeListSize; stats.freeSize += freeListSize; + stats.totalCollected = .totalCollectedPages * PAGESIZE; + } + + void enableProfiling() nothrow @nogc + { + this.profiling_enabled = true; + } + + shared(Mutex) profilerLock() nothrow @nogc + { + return this.profiler_lock; } } @@ -2434,7 +2462,9 @@ struct Gcx updateCollectThresholds(); - return freedLargePages + freedSmallPages; + auto total = freedLargePages + freedSmallPages; + totalCollectedPages += total; + return total; } /** diff --git a/src/gc/impl/manual/gc.d b/src/gc/impl/manual/gc.d index 84712d410d..a768cd4ce7 100644 --- a/src/gc/impl/manual/gc.d +++ b/src/gc/impl/manual/gc.d @@ -31,6 +31,7 @@ import gc.gcinterface; import rt.util.container.array; import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc; +import core.sync.mutex; static import core.memory; extern (C) void onOutOfMemoryError(void* pretend_sideffect = null) @trusted pure nothrow @nogc; /* dmd @@@BUG11461@@@ */ @@ -271,4 +272,13 @@ class ManualGC : GC { return false; } + + void enableProfiling() nothrow @nogc + { + } + + shared(Mutex) profilerLock() nothrow @nogc + { + return null; + } } diff --git a/src/rt/profilegc.d b/src/rt/profilegc.d index a3c5ce6235..f0a1d46949 100644 --- a/src/rt/profilegc.d +++ b/src/rt/profilegc.d @@ -21,7 +21,7 @@ import core.stdc.string; import core.exception : onOutOfMemoryError; -struct Entry { size_t count, size; } +struct Entry { ulong count, size; } char[] buffer; Entry[string] newCounts; @@ -41,12 +41,15 @@ __gshared extern (C) void profilegc_setlogfilename(string name) { + import gc.proxy : gc_getProxy; + gc_getProxy().enableProfiling(); logfilename = name; } -public void accumulate(string file, uint line, string funcname, string type, size_t sz) +public void accumulate(string file, uint line, string funcname, string type, + ulong sz) { char[3 * line.sizeof + 1] buf; auto buflen = snprintf(buf.ptr, buf.length, "%u", line); @@ -85,23 +88,23 @@ public void accumulate(string file, uint line, string funcname, string type, siz // Merge thread local newCounts into globalNewCounts static ~this() { + import gc.proxy : gc_getProxy; + auto mtx = gc_getProxy().profilerLock(); + if (newCounts.length) { - synchronized + mtx.lock(); + scope(exit) mtx.unlock(); + + foreach (name, entry; newCounts) { - if (globalNewCounts.length) - { - // Merge - foreach (name, entry; newCounts) - { - globalNewCounts[name].count += entry.count; - globalNewCounts[name].size += entry.size; - } - } - else - // Assign - globalNewCounts = newCounts; + if (!(name in globalNewCounts)) + globalNewCounts[name] = Entry.init; + + globalNewCounts[name].count += entry.count; + globalNewCounts[name].size += entry.size; } + newCounts = null; } free(buffer.ptr); @@ -121,10 +124,15 @@ shared static ~this() { auto result1 = cast(Result*)r1; auto result2 = cast(Result*)r2; - ptrdiff_t cmp = result2.entry.size - result1.entry.size; + auto totalSize1 = result1.entry.size * result1.entry.count; + auto totalSize2 = result2.entry.size * result2.entry.count; + long cmp = totalSize2 - totalSize1; + if (cmp) return cmp < 0 ? -1 : 1; + cmp = result2.entry.size - result1.entry.size; if (cmp) return cmp < 0 ? -1 : 1; cmp = result2.entry.count - result1.entry.count; - return cmp < 0 ? -1 : (cmp > 0 ? 1 : 0); + if (cmp) return cmp < 0 ? -1 : 1; + return result2.name < result1.name; } } diff --git a/src/rt/tracegc.d b/src/rt/tracegc.d index 708ca347be..49fb4c1d1f 100644 --- a/src/rt/tracegc.d +++ b/src/rt/tracegc.d @@ -3,6 +3,8 @@ * -profile=gc * switch is thrown. * + * Tests for this functionality can be found in test/profile/src/profilegc.d + * * Copyright: Copyright Digital Mars 2015 - 2015. * License: Distributed under the * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). @@ -13,523 +15,170 @@ module rt.tracegc; -//version = tracegc; - -import rt.profilegc; - -version (tracegc) import core.stdc.stdio; - -version (none) -{ - // this exercises each function - - struct S { ~this() { } } - class C { } - interface I { } - - void main() - { - { - auto a = new C(); - auto b = new int; - auto c = new int[3]; - auto d = new int[][](3,4); - auto e = new float; - auto f = new float[3]; - auto g = new float[][](3,4); - } - printf("\n"); - { - int[] a; delete a; - S[] as; delete as; - C c; delete c; - I i; delete i; - C* pc = &c; delete *pc; - I* pi = &i; delete *pi; - int* pint; delete pint; - S* ps; delete ps; - } - printf("\n"); - { - int[] a = [1, 2, 3]; - string[int] aa = [1:"one", 2:"two", 3:"three"]; - } - printf("\n"); - { - int[] a, b, c; - c = a ~ b; - c = a ~ b ~ c; - } - printf("\n"); - { - dchar dc = 'a'; - char[] ac; ac ~= dc; - wchar[] aw; aw ~= dc; - char[] ac2; ac2 ~= ac; - int[] ai; ai ~= 3; - } - printf("\n"); - { - int[] ai; ai.length = 10; - float[] af; af.length = 10; - } - printf("\n"); - int v; - { - int foo() { return v; } - static int delegate() dg; - dg = &foo; // dynamic closure - } - } -} +// version = tracegc; extern (C) Object _d_newclass(const ClassInfo ci); extern (C) void[] _d_newarrayT(const TypeInfo ti, size_t length); extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length); extern (C) void[] _d_newarraymTX(const TypeInfo ti, size_t[] dims); extern (C) void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims); -extern (C) void* _d_newitemT(in TypeInfo _ti); -extern (C) void* _d_newitemiT(in TypeInfo _ti); - -extern (C) Object _d_newclassTrace(string file, int line, string funcname, const ClassInfo ci) -{ - version (tracegc) - { - printf("_d_newclassTrace class = %s file = '%.*s' line = %d function = '%.*s'\n", - cast(char *)ci.name, - file.length, file.ptr, - line, - funcname.length, funcname.ptr - ); - } - accumulate(file, line, funcname, ci.name, ci.initializer.length); - return _d_newclass(ci); -} - -extern (C) void[] _d_newarrayTTrace(string file, int line, string funcname, const TypeInfo ti, size_t length) -{ - version (tracegc) - { - printf("_d_newarrayTTrace type = %s length = %llu file = '%.*s' line = %d function = '%.*s'\n", - cast(char *)ti.toString().ptr, - cast(ulong)length, - file.length, file.ptr, - line, - funcname.length, funcname.ptr - ); - } - accumulate(file, line, funcname, ti.toString(), ti.tsize * length); - return _d_newarrayT(ti, length); -} - -extern (C) void[] _d_newarrayiTTrace(string file, int line, string funcname, const TypeInfo ti, size_t length) -{ - version (tracegc) - { - printf("_d_newarrayiTTrace type = %s length = %llu file = '%.*s' line = %d function = '%.*s'\n", - cast(char *)ti.toString().ptr, - cast(ulong)length, - file.length, file.ptr, - line, - funcname.length, funcname.ptr - ); - } - accumulate(file, line, funcname, ti.toString(), ti.tsize * length); - return _d_newarrayiT(ti, length); -} - -extern (C) void[] _d_newarraymTXTrace(string file, int line, string funcname, const TypeInfo ti, size_t[] dims) -{ - version (tracegc) - { - printf("_d_newarraymTXTrace type = %s dims = %llu file = '%.*s' line = %d function = '%.*s'\n", - cast(char *)ti.toString().ptr, - cast(ulong)dims.length, - file.length, file.ptr, - line, - funcname.length, funcname.ptr - ); - } - size_t n = 1; - foreach (dim; dims) - n *= dim; - accumulate(file, line, funcname, ti.toString(), ti.tsize * n); - return _d_newarraymTX(ti, dims); -} - -extern (C) void[] _d_newarraymiTXTrace(string file, int line, string funcname, const TypeInfo ti, size_t[] dims) -{ - version (tracegc) - { - printf("_d_newarraymiTXTrace type = %s dims = %llu file = '%.*s' line = %d function = '%.*s'\n", - cast(char *)ti.toString().ptr, - cast(ulong)dims.length, - file.length, file.ptr, - line, - funcname.length, funcname.ptr - ); - } - size_t n = 1; - foreach (dim; dims) - n *= dim; - accumulate(file, line, funcname, ti.toString(), ti.tsize * n); - return _d_newarraymiTX(ti, dims); -} - -extern (C) void* _d_newitemTTrace(string file, int line, string funcname, in TypeInfo ti) -{ - version (tracegc) - { - printf("_d_newitemTTrace type = %s file = '%.*s' line = %d function = '%.*s'\n", - cast(char *)ti.toString().ptr, - file.length, file.ptr, - line, - funcname.length, funcname.ptr - ); - } - accumulate(file, line, funcname, ti.toString(), ti.tsize); - return _d_newitemT(ti); -} - -extern (C) void* _d_newitemiTTrace(string file, int line, string funcname, in TypeInfo ti) -{ - version (tracegc) - { - printf("_d_newitemiTTrace type = %s file = '%.*s' line = %d function = '%.*s'\n", - cast(char *)ti.toString().ptr, - file.length, file.ptr, - line, - funcname.length, funcname.ptr - ); - } - accumulate(file, line, funcname, ti.toString(), ti.tsize); - return _d_newitemiT(ti); -} - - +extern (C) void* _d_newitemT(in TypeInfo ti); +extern (C) void* _d_newitemiT(in TypeInfo ti); extern (C) void _d_callfinalizer(void* p); extern (C) void _d_callinterfacefinalizer(void *p); extern (C) void _d_delclass(Object* p); extern (C) void _d_delinterface(void** p); extern (C) void _d_delstruct(void** p, TypeInfo_Struct inf); -extern (C) void _d_delarray_t(void[]* p, const TypeInfo_Struct ti); +extern (C) void _d_delarray_t(void[]* p, const TypeInfo_Struct _); extern (C) void _d_delmemory(void* *p); +extern (C) byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y); +extern (C) void[] _d_arraycatnTX(const TypeInfo ti, byte[][] arrs); +extern (C) void* _d_arrayliteralTX(const TypeInfo ti, size_t length); +extern (C) void* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, + void[] keys, void[] vals); +extern (C) void[] _d_arrayappendT(const TypeInfo ti, ref byte[] x, byte[] y); +extern (C) byte[] _d_arrayappendcTX(const TypeInfo ti, ref byte[] px, size_t n); +extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c); +extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c); +extern (C) void[] _d_arraysetlengthT(const TypeInfo ti, size_t newlength, void[]* p); +extern (C) void[] _d_arraysetlengthiT(const TypeInfo ti, size_t newlength, void[]* p); +extern (C) void* _d_allocmemory(size_t sz); -extern (C) void _d_callfinalizerTrace(string file, int line, string funcname, void* p) -{ - version (tracegc) - { - printf("_d_callfinalizerTrace %p file = '%.*s' line = %d function = '%.*s'\n", - p, - file.length, file.ptr, - line, - funcname.length, funcname.ptr - ); - } - _d_callfinalizer(p); -} - -extern (C) void _d_callinterfacefinalizerTrace(string file, int line, string funcname, void *p) -{ - version (tracegc) - { - printf("_d_callinterfacefinalizerTrace %p file = '%.*s' line = %d function = '%.*s'\n", - p, - file.length, file.ptr, - line, - funcname.length, funcname.ptr - ); - } - _d_callinterfacefinalizer(p); -} - -extern (C) void _d_delclassTrace(string file, int line, string funcname, Object* p) -{ - version (tracegc) - { - printf("_d_delclassTrace %p file = '%.*s' line = %d function = '%.*s'\n", - *p, - file.length, file.ptr, - line, - funcname.length, funcname.ptr - ); - } - _d_delclass(p); -} - -extern (C) void _d_delinterfaceTrace(string file, int line, string funcname, void** p) -{ - version (tracegc) - { - printf("_d_delinterfaceTrace %p file = '%.*s' line = %d function = '%.*s'\n", - *p, - file.length, file.ptr, - line, - funcname.length, funcname.ptr - ); - } - _d_delinterface(p); -} - -extern (C) void _d_delstructTrace(string file, int line, string funcname, void** p, TypeInfo_Struct inf) -{ - version (tracegc) - { - printf("_d_delstructTrace %p type = %s file = '%.*s' line = %d function = '%.*s'\n", - *p, - cast(char *)inf.toString().ptr, - file.length, file.ptr, - line, - funcname.length, funcname.ptr - ); - } - _d_delstruct(p, inf); -} +// Used as wrapper function body to get actual stats. Calling `original_func()` +// will call wrapped function with all required arguments. +// +// Placed here as a separate string constant to simplify maintenance as it is +// much more likely to be modified than rest of generation code. +enum accumulator = q{ + import rt.profilegc : accumulate; + import core.memory : GC; + + static if (is(typeof(ci))) + string name = ci.name; + else static if (is(typeof(ti))) + string name = ti.toString(); + else static if (__FUNCTION__ == "rt.tracegc._d_arrayappendcdTrace") + string name = "char[]"; + else static if (__FUNCTION__ == "rt.tracegc._d_arrayappendwdTrace") + string name = "wchar[]"; + else static if (__FUNCTION__ == "rt.tracegc._d_allocmemoryTrace") + string name = "closure"; + else + string name = ""; -extern (C) void _d_delarray_tTrace(string file, int line, string funcname, void[]* p, const TypeInfo_Struct ti) -{ - version (tracegc) + version(tracegc) { - printf("_d_delarray_tTrace %p[%llu] type = %s file = '%.*s' line = %d function = '%.*s'\n", - (*p).ptr, cast(ulong)(*p).length, - ti ? cast(char *)ti.toString().ptr : cast(char*)"".ptr, - file.length, file.ptr, - line, - funcname.length, funcname.ptr - ); - } - _d_delarray_t(p, ti); -} + import core.stdc.stdio; -extern (C) void _d_delmemoryTrace(string file, int line, string funcname, void* *p) -{ - version (tracegc) - { - printf("_d_delmemoryTrace %p file = '%.*s' line = %d function = '%.*s'\n", - *p, + printf("%s file = '%.*s' line = %d function = '%.*s' type = %.*s\n", + __FUNCTION__.ptr, file.length, file.ptr, line, - funcname.length, funcname.ptr - ); + funcname.length, funcname.ptr, + name.length, name.ptr + ); } - _d_delmemory(p); -} + import gc.proxy : gc_getProxy; + auto mtx = gc_getProxy().profilerLock(); -extern (C) void* _d_arrayliteralTX(const TypeInfo ti, size_t length); -extern (C) void* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys, void[] vals); + mtx.lock(); + scope(exit) mtx.unlock(); -extern (C) void* _d_arrayliteralTXTrace(string file, int line, string funcname, const TypeInfo ti, size_t length) -{ - version (tracegc) + auto stats1 = GC.stats(); + scope(exit) { - printf("_d_arrayliteralTXTrace type = %s length = %llu file = '%.*s' line = %d function = '%.*s'\n", - cast(char *)ti.toString().ptr, - cast(ulong)length, - file.length, file.ptr, - line, - funcname.length, funcname.ptr - ); - } - accumulate(file, line, funcname, ti.toString(), ti.next.tsize * length); - return _d_arrayliteralTX(ti, length); -} + auto stats2 = GC.stats(); + if (stats2.totalCollected < stats1.totalCollected) + { + // need to account for unsigned overflow possibility if app is being + // run very long + stats2.totalCollected += typeof(stats1.totalCollected).max + - stats1.totalCollected; + stats1.totalCollected = 0; + } -extern (C) void* _d_assocarrayliteralTXTrace(string file, int line, string funcname, - const TypeInfo_AssociativeArray ti, void[] keys, void[] vals) -{ - version (tracegc) - { - printf("_d_assocarrayliteralTXTrace type = %s keys = %llu values = %llu file = '%.*s' line = %d function = '%.*s'\n", - cast(char *)ti.toString().ptr, - cast(ulong)keys.length, - cast(ulong)vals.length, - file.length, file.ptr, - line, - funcname.length, funcname.ptr - ); + ulong size = (stats2.usedSize + stats2.totalCollected) - + (stats1.usedSize + stats1.totalCollected); + if (size > 0) + accumulate(file, line, funcname, name, size); } - accumulate(file, line, funcname, ti.toString(), (ti.key.tsize + ti.value.tsize) * keys.length); - return _d_assocarrayliteralTX(ti, keys, vals); -} + return original_func(); +}; +mixin(generateTraceWrappers()); +//pragma(msg, generateTraceWrappers()); -extern (C) byte[] _d_arraycatT(const TypeInfo ti, byte[] x, byte[] y); -extern (C) void[] _d_arraycatnTX(const TypeInfo ti, byte[][] arrs); +//////////////////////////////////////////////////////////////////////////////// +// code gen implementation -extern (C) byte[] _d_arraycatTTrace(string file, int line, string funcname, const TypeInfo ti, byte[] x, byte[] y) +private string generateTraceWrappers() { - version (tracegc) - { - printf("_d_arraycatT type = %s x = %llu y = %llu file = '%.*s' line = %d function = '%.*s'\n", - cast(char *)ti.toString().ptr, - cast(ulong)x.length, - cast(ulong)y.length, - file.length, file.ptr, - line, - funcname.length, funcname.ptr - ); - } - accumulate(file, line, funcname, ti.toString(), (x.length + y.length) * ti.next.tsize); - return _d_arraycatT(ti, x, y); -} + string code; -extern (C) void[] _d_arraycatnTXTrace(string file, int line, string funcname, const TypeInfo ti, byte[][] arrs) -{ - version (tracegc) + foreach (name; __traits(allMembers, mixin(__MODULE__))) { - printf("_d_arraycatnTX type = %s arrs = %llu file = '%.*s' line = %d function = '%.*s'\n", - cast(char *)ti.toString().ptr, - cast(ulong)arrs.length, - file.length, file.ptr, - line, - funcname.length, funcname.ptr - ); + static if (name.length > 3 && name[0..3] == "_d_") + { + mixin("alias Declaration = " ~ name ~ ";"); + code ~= generateWrapper!Declaration(); + } } - size_t length; - foreach (b; arrs) - length += b.length; - accumulate(file, line, funcname, ti.toString(), length * ti.next.tsize); - return _d_arraycatnTX(ti, arrs); -} -extern (C) void[] _d_arrayappendT(const TypeInfo ti, ref byte[] x, byte[] y); -extern (C) byte[] _d_arrayappendcTX(const TypeInfo ti, ref byte[] px, size_t n); -extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c); -extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c); - -extern (C) void[] _d_arrayappendTTrace(string file, int line, string funcname, const TypeInfo ti, ref byte[] x, byte[] y) -{ - version (tracegc) - { - printf("_d_arrayappendT type = %s x = %llu y = %llu file = '%.*s' line = %d function = '%.*s'\n", - cast(char *)ti.toString().ptr, - cast(ulong)x.length, - cast(ulong)y.length, - file.length, file.ptr, - line, - funcname.length, funcname.ptr - ); - } - accumulate(file, line, funcname, ti.toString(), ti.next.tsize * y.length); - return _d_arrayappendT(ti, x, y); + return code; } -extern (C) byte[] _d_arrayappendcTXTrace(string file, int line, string funcname, const TypeInfo ti, ref byte[] px, size_t n) +private string generateWrapper(alias Declaration)() { - version (tracegc) + static size_t findParamIndex(string s) { - printf("_d_arrayappendcTX type = %s x = %llu n = %llu file = '%.*s' line = %d function = '%.*s'\n", - cast(char *)ti.toString().ptr, - cast(ulong)px.length, - cast(ulong)n, - file.length, file.ptr, - line, - funcname.length, funcname.ptr - ); - } - accumulate(file, line, funcname, ti.toString(), ti.next.tsize * n); - return _d_arrayappendcTX(ti, px, n); -} + assert (s[$-1] == ')'); + size_t brackets = 1; + while (brackets != 0) + { + s = s[0 .. $-1]; + if (s[$-1] == ')') + ++brackets; + if (s[$-1] == '(') + --brackets; + } -extern (C) void[] _d_arrayappendcdTrace(string file, int line, string funcname, ref byte[] x, dchar c) -{ - version (tracegc) - { - printf("_d_arrayappendcd x = %llu c = x%x file = '%.*s' line = %d function = '%.*s'\n", - cast(ulong)x.length, - c, - file.length, file.ptr, - line, - funcname.length, funcname.ptr - ); + assert(s.length > 1); + return s.length - 1; } - size_t n; - if (c <= 0x7F) - n = 1; - else if (c <= 0x7FF) - n = 2; - else if (c <= 0xFFFF) - n = 3; - else if (c <= 0x10FFFF) - n = 4; - else - assert(0); - accumulate(file, line, funcname, "char[]", n * char.sizeof); - return _d_arrayappendcd(x, c); -} -extern (C) void[] _d_arrayappendwdTrace(string file, int line, string funcname, ref byte[] x, dchar c) -{ - version (tracegc) - { - printf("_d_arrayappendwd x = %llu c = x%x file = '%.*s' line = %d function = '%.*s'\n", - cast(ulong)x.length, - c, - file.length, file.ptr, - line, - funcname.length, funcname.ptr - ); - } - size_t n = 1 + (c > 0xFFFF); - accumulate(file, line, funcname, "wchar[]", n * wchar.sizeof); - return _d_arrayappendwd(x, c); -} + auto type_string = typeof(Declaration).stringof; + auto name = __traits(identifier, Declaration); + auto param_idx = findParamIndex(type_string); -extern (C) void[] _d_arraysetlengthT(const TypeInfo ti, size_t newlength, void[]* p); -extern (C) void[] _d_arraysetlengthiT(const TypeInfo ti, size_t newlength, void[]* p); + auto new_declaration = type_string[0 .. param_idx] ~ " " ~ name + ~ "Trace(string file, int line, string funcname, " + ~ type_string[param_idx+1 .. $]; + auto call_original = " scope original_func = { return " + ~ __traits(identifier, Declaration) ~ "(" ~ Arguments!Declaration() ~ "); };"; -extern (C) void[] _d_arraysetlengthTTrace(string file, int line, string funcname, const TypeInfo ti, size_t newlength, void[]* p) -{ - version (tracegc) - { - printf("_d_arraysetlengthT type = %s length = %llu newlength = %llu file = '%.*s' line = %d function = '%.*s'\n", - cast(char *)ti.toString().ptr, - cast(ulong)(*p).length, - cast(ulong)newlength, - file.length, file.ptr, - line, - funcname.length, funcname.ptr - ); - } - accumulate(file, line, funcname, ti.toString(), ti.next.tsize * newlength); - return _d_arraysetlengthT(ti, newlength, p); + return new_declaration ~ "\n{\n" ~ + call_original ~ "\n" ~ + accumulator ~ "\n" ~ + "}\n"; } -extern (C) void[] _d_arraysetlengthiTTrace(string file, int line, string funcname, const TypeInfo ti, size_t newlength, void[]* p) +string Arguments(alias Func)() { - version (tracegc) + string result = ""; + + static if (is(typeof(Func) PT == __parameters)) { - printf("_d_arraysetlengthiT type = %s length = %llu newlength = %llu file = '%.*s' line = %d function = '%.*s'\n", - cast(char *)ti.toString().ptr, - cast(ulong)(*p).length, - cast(ulong)newlength, - file.length, file.ptr, - line, - funcname.length, funcname.ptr - ); + foreach (idx, _; PT) + result ~= __traits(identifier, PT[idx .. idx + 1]) ~ ", "; } - accumulate(file, line, funcname, ti.toString(), ti.next.tsize * newlength); - return _d_arraysetlengthiT(ti, newlength, p); -} - -extern (C) void* _d_allocmemory(size_t sz); + return result; +} -extern (C) void* _d_allocmemoryTrace(string file, int line, string funcname, size_t sz) +unittest { - version (tracegc) - { - printf("_d_allocmemory sz = %llu file = '%.*s' line = %d function = '%.*s'\n", - cast(ulong)sz, - file.length, file.ptr, - line, - funcname.length, funcname.ptr - ); - } - accumulate(file, line, funcname, "closure", sz); - return _d_allocmemory(sz); + void foo(int x, double y) { } + static assert (Arguments!foo == "x, y, "); } - - diff --git a/test/profile/bothgc.log.exp b/test/profile/bothgc.log.exp index 6d389adbac..884f24c2a3 100644 --- a/test/profile/bothgc.log.exp +++ b/test/profile/bothgc.log.exp @@ -1,2 +1,2 @@ bytes allocated, allocations, type, function, file:line - 4000 1000 both.Num both.foo src/both.d:15 + 16000 1000 both.Num both.foo src/both.d:15 diff --git a/test/profile/myprofilegc.log.exp b/test/profile/myprofilegc.log.exp index f2337d8c54..34fb40aa61 100644 --- a/test/profile/myprofilegc.log.exp +++ b/test/profile/myprofilegc.log.exp @@ -1,2 +1,20 @@ bytes allocated, allocations, type, function, file:line - 4 1 uint D main src/profilegc.d:6 + 2560 10 core.thread.Thread D main src/profilegc.d:77 + 640 10 int[] profilegc.main.bar src/profilegc.d:73 + 240 4 core.thread.Thread[] D main src/profilegc.d:77 + 288 1 immutable(char)[][int] D main src/profilegc.d:34 + 160 1 float[][] D main src/profilegc.d:18 + 160 1 int[][] D main src/profilegc.d:15 + 64 1 float[] D main src/profilegc.d:53 + 64 1 int[] D main src/profilegc.d:52 + 32 1 profilegc.main.C D main src/profilegc.d:12 + 16 1 char[] D main src/profilegc.d:45 + 16 1 char[] D main src/profilegc.d:47 + 16 1 closure profilegc.main.foo src/profilegc.d:57 + 16 1 float D main src/profilegc.d:16 + 16 1 float[] D main src/profilegc.d:17 + 16 1 int D main src/profilegc.d:13 + 16 1 int[] D main src/profilegc.d:14 + 16 1 int[] D main src/profilegc.d:33 + 16 1 int[] D main src/profilegc.d:48 + 16 1 wchar[] D main src/profilegc.d:46 diff --git a/test/profile/src/profilegc.d b/test/profile/src/profilegc.d index 25e76fbe68..22a9676db2 100644 --- a/test/profile/src/profilegc.d +++ b/test/profile/src/profilegc.d @@ -3,5 +3,80 @@ import core.runtime; void main(string[] args) { profilegc_setlogfilename(args[1]); - auto p = new uint; + + struct S { ~this() { } } + class C { } + interface I { } + + { + auto a = new C(); + auto b = new int; + auto c = new int[3]; + auto d = new int[][](3,4); + auto e = new float; + auto f = new float[3]; + auto g = new float[][](3,4); + } + + { + int[] a; delete a; + S[] as; delete as; + C c; delete c; + I i; delete i; + C* pc = &c; delete *pc; + I* pi = &i; delete *pi; + int* pint; delete pint; + S* ps; delete ps; + } + + { + int[] a = [1, 2, 3]; + string[int] aa = [1:"one", 2:"two", 3:"three"]; + } + + { + int[] a, b, c; + c = a ~ b; + c = a ~ b ~ c; + } + + { + dchar dc = 'a'; + char[] ac; ac ~= dc; + wchar[] aw; aw ~= dc; + char[] ac2; ac2 ~= ac; + int[] ai; ai ~= 3; + } + + { + int[] ai; ai.length = 10; + float[] af; af.length = 10; + } + + { + auto foo ( ) + { + int v = 42; + return { return v; }; + } + + auto x = foo()(); + } + + { + import core.thread; + + Thread[] arr; + + void bar ( ) + { + auto x = new int[10]; + } + + for (int i = 0; i < 10; ++i) + arr ~= new Thread(&bar, 1024).start(); + + foreach (t; arr) + t.join(); + } }