digitalmars.D - GC profiling upon exit()
- =?UTF-8?Q?Ali_=c3=87ehreli?= (7/7) Aug 11 2016 I've noticed that GC profiling[1] is not provided if the application
- Jonathan M Davis via Digitalmars-d (8/15) Aug 11 2016 Well, you could register stuff with atexit() so that it runs when exit()...
- =?UTF-8?Q?Ali_=c3=87ehreli?= (15/34) Aug 11 2016 exit() is
- =?UTF-8?Q?Ali_=c3=87ehreli?= (112/116) Aug 11 2016 Instead of coming up with a design and fixing druntime, for now I hacked...
I've noticed that GC profiling[1] is not provided if the application exit()s as opposed to returns. Is that intended? Would it be possible to print it still? Related, stdout may not be the best place for that information: ;) https://issues.dlang.org/show_bug.cgi?id=15602 Ali [1] https://dlang.org/spec/garbage.html#gc_config
Aug 11 2016
On Thursday, August 11, 2016 02:17:06 Ali Çehreli via Digitalmars-d wrote:I've noticed that GC profiling[1] is not provided if the application exit()s as opposed to returns. Is that intended? Would it be possible to print it still? Related, stdout may not be the best place for that information: ;) https://issues.dlang.org/show_bug.cgi?id=15602 Ali [1] https://dlang.org/spec/garbage.html#gc_configWell, you could register stuff with atexit() so that it runs when exit() is called, but calling exit() isn't much better than hitting assert(0) or using ctrl-c. exit() doesn't shut things down even vaguely cleanly. So, you _might_ be able to get something thanks to atexit(), but I'm not sure how much you can really do given that your program has basically just been shot in the head. - Jonathan M Davis
Aug 11 2016
On 08/11/2016 02:56 AM, Jonathan M Davis via Digitalmars-d wrote:On Thursday, August 11, 2016 02:17:06 Ali Çehreli via Digitalmars-dwrote:exit() isI've noticed that GC profiling[1] is not provided if the application exit()s as opposed to returns. Is that intended? Would it be possible to print it still? Related, stdout may not be the best place for that information: ;) https://issues.dlang.org/show_bug.cgi?id=15602 Ali [1] https://dlang.org/spec/garbage.html#gc_configWell, you could register stuff with atexit() so that it runs whencalled, but calling exit() isn't much better than hitting assert(0)or usingctrl-c. exit() doesn't shut things down even vaguely cleanly.Agreed that assert(0) is fatal but Ctrl-C is just a signal that can be handled cleanly. exit() is not necessarily an error return. It just means that the application does not care about regular cleanup.So, you _might_ be able to get something thanks to atexit(), but I'm not sure how much you can really do given that your program has basically justbeen shotin the head.The good thing is that the application may be calling exit() itself, where it could have made a call to get the profile information. The main issue here is that the output of GC profile information is hardwired to GC destruction (and stdout). I've opened this enhancement request: https://issues.dlang.org/show_bug.cgi?id=16377 Ali
Aug 11 2016
On 08/11/2016 02:34 PM, Ali Çehreli wrote:the application may be calling exit() itself, where it could have made a call to get the profile information. The main issue here is that the output of GC profile information is hardwired to GC destruction (and stdout).Instead of coming up with a design and fixing druntime, for now I hacked it by 1) Declaring druntime's profiling variables with their mangled names 2) Copying and modifying the druntime code that dumps stats so that it accepts any FILE* Sorry to dump this code here but maybe others will need it as well. The program must be started by turning GC profiling on: $ ./foo "--DRT-gcopt=profile:1" (See https://dlang.org/spec/garbage.html#gc_config ) Ali /* HACK: A helper mixin that converts pairs of types and symbols to their corresponding declarations accessible from druntime. For example, the following definition is produced for the pair of (int, "i"): extern(C) pragma(mangle, mangle!int("gc.gc.i")) extern __gshared int i; */ mixin template declareGCProfVars(A...) { string definitions() { string result; string currentType; /* static */ foreach(i, a; A) { static if ((i % 2) == 0) { static assert(is(a), "Expected a type, not " ~ a.stringof); currentType = a.stringof; // Save for the next iteration } else { static assert (is(typeof(a) == string), "Expected string, not " ~ a.stringof); result ~= `extern(C) pragma(mangle, mangle!` ~ currentType ~ `("gc.gc.` ~ a ~ `")) extern __gshared ` ~ currentType ~ ` ` ~ a ~ `;`; currentType = ""; // play safe } } return result; } import core.demangle; mixin (definitions()); } /* List of GC profiling variables that druntime uses as of 2.071. Copied from src/gc/impl/conservative/gc.d of * https://github.com/dlang/druntime */ import std.datetime : Duration; mixin declareGCProfVars!( Duration, "prepTime", Duration, "markTime", Duration, "sweepTime", Duration, "recoverTime", Duration, "maxPauseTime", size_t, "numCollections", size_t, "maxPoolMemory", ); public import std.stdio : FILE; void dumpGCProfile(FILE *file) { import std.stdio : fprintf; /* Adapted from src/gc/impl/conservative/gc.d of https://github.com/dlang/druntime */ fprintf(file, "\tNumber of collections: %llu\n", cast(ulong)numCollections); fprintf(file, "\tTotal GC prep time: %lld milliseconds\n", prepTime.total!("msecs")); fprintf(file, "\tTotal mark time: %lld milliseconds\n", markTime.total!("msecs")); fprintf(file, "\tTotal sweep time: %lld milliseconds\n", sweepTime.total!("msecs")); fprintf(file, "\tTotal page recovery time: %lld milliseconds\n", recoverTime.total!("msecs")); const long maxPause = maxPauseTime.total!("msecs"); fprintf(file, "\tMax Pause Time: %lld milliseconds\n", maxPause); const long gcTime = (recoverTime + sweepTime + markTime + prepTime).total!("msecs"); fprintf(file, "\tGrand total GC time: %lld milliseconds\n", gcTime); const long pauseTime = (markTime + prepTime).total!("msecs"); fprintf(file, "GC summary:%5lld MB,%5lld GC%5lld ms, Pauses%5lld ms <%5lld ms\n", cast(long) maxPoolMemory >> 20, cast(ulong)numCollections, gcTime, pauseTime, maxPause); } bool isGCProfOn() { import core.runtime : Runtime; import std.regex : ctRegex, match; import std.algorithm : any; /* Although the GC documentation says that the program arguments "are still available via rt_args"[1], they seem to * be available only through Runtime.args(). * * [1] https://dlang.org/spec/garbage.html#gc_config */ enum r = ctRegex!"--DRT-gcopt.*profile:1"; return Runtime.args().any!(arg => arg.match(r)); } import std.range; import std.algorithm; import std.stdio; import core.stdc.stdlib; void main() { // Cause some allocations size_t total; iota(1000).each!(i => total += iota(1000).map!(j => new int[10000]).array.length); writeln(total); /* Let's assume that the program is about to exit() but we're still interested in GC stats. First ensure that the * stats are requested on the command line. */ if (isGCProfOn()) { dumpGCProfile(stderr.getFP()); } exit(0); }
Aug 11 2016