GC profiling upon exit()

Ali Çehreli via Digitalmars-d digitalmars-d at puremagic.com
Thu Aug 11 18:32:13 PDT 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);
}



More information about the Digitalmars-d mailing list