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);
}

Reply via email to