Home | History | Annotate | Line # | Download | only in rt
profilegc.d revision 1.1
      1  1.1  mrg /*
      2  1.1  mrg  * Data collection and report generation for
      3  1.1  mrg  *   -profile=gc
      4  1.1  mrg  * switch
      5  1.1  mrg  *
      6  1.1  mrg  * Copyright: Copyright Digital Mars 2015 - 2015.
      7  1.1  mrg  * License: Distributed under the
      8  1.1  mrg  *      $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
      9  1.1  mrg  *    (See accompanying file LICENSE)
     10  1.1  mrg  * Authors:   Andrei Alexandrescu and Walter Bright
     11  1.1  mrg  * Source: $(DRUNTIMESRC rt/_profilegc.d)
     12  1.1  mrg  */
     13  1.1  mrg 
     14  1.1  mrg module rt.profilegc;
     15  1.1  mrg 
     16  1.1  mrg private:
     17  1.1  mrg 
     18  1.1  mrg import core.stdc.stdio;
     19  1.1  mrg import core.stdc.stdlib;
     20  1.1  mrg import core.stdc.string;
     21  1.1  mrg 
     22  1.1  mrg import core.exception : onOutOfMemoryError;
     23  1.1  mrg import core.internal.container.hashtab;
     24  1.1  mrg 
     25  1.1  mrg struct Entry { ulong count, size; }
     26  1.1  mrg 
     27  1.1  mrg char[] buffer;
     28  1.1  mrg HashTab!(const(char)[], Entry) newCounts;
     29  1.1  mrg 
     30  1.1  mrg __gshared
     31  1.1  mrg {
     32  1.1  mrg     HashTab!(const(char)[], Entry) globalNewCounts;
     33  1.1  mrg     string logfilename = "profilegc.log";
     34  1.1  mrg }
     35  1.1  mrg 
     36  1.1  mrg /****
     37  1.1  mrg  * Set file name for output.
     38  1.1  mrg  * A file name of "" means write results to stdout.
     39  1.1  mrg  * Params:
     40  1.1  mrg  *      name = file name
     41  1.1  mrg  */
     42  1.1  mrg 
     43  1.1  mrg extern (C) void profilegc_setlogfilename(string name)
     44  1.1  mrg {
     45  1.1  mrg     logfilename = name ~ "\0";
     46  1.1  mrg }
     47  1.1  mrg 
     48  1.1  mrg public void accumulate(string file, uint line, string funcname, string type, ulong sz) @nogc nothrow
     49  1.1  mrg {
     50  1.1  mrg     if (sz == 0)
     51  1.1  mrg         return;
     52  1.1  mrg 
     53  1.1  mrg     char[3 * line.sizeof + 1] buf = void;
     54  1.1  mrg     auto buflen = snprintf(buf.ptr, buf.length, "%u", line);
     55  1.1  mrg 
     56  1.1  mrg     auto length = type.length + 1 + funcname.length + 1 + file.length + 1 + buflen;
     57  1.1  mrg     if (length > buffer.length)
     58  1.1  mrg     {
     59  1.1  mrg         // Enlarge buffer[] so it is big enough
     60  1.1  mrg         assert(buffer.length > 0 || buffer.ptr is null);
     61  1.1  mrg         auto p = cast(char*)realloc(buffer.ptr, length);
     62  1.1  mrg         if (!p)
     63  1.1  mrg             onOutOfMemoryError();
     64  1.1  mrg         buffer = p[0 .. length];
     65  1.1  mrg     }
     66  1.1  mrg 
     67  1.1  mrg     // "type funcname file:line"
     68  1.1  mrg     buffer[0 .. type.length] = type[];
     69  1.1  mrg     buffer[type.length] = ' ';
     70  1.1  mrg     buffer[type.length + 1 ..
     71  1.1  mrg            type.length + 1 + funcname.length] = funcname[];
     72  1.1  mrg     buffer[type.length + 1 + funcname.length] = ' ';
     73  1.1  mrg     buffer[type.length + 1 + funcname.length + 1 ..
     74  1.1  mrg            type.length + 1 + funcname.length + 1 + file.length] = file[];
     75  1.1  mrg     buffer[type.length + 1 + funcname.length + 1 + file.length] = ':';
     76  1.1  mrg     buffer[type.length + 1 + funcname.length + 1 + file.length + 1 ..
     77  1.1  mrg            type.length + 1 + funcname.length + 1 + file.length + 1 + buflen] = buf[0 .. buflen];
     78  1.1  mrg 
     79  1.1  mrg     if (auto pcount = cast(string)buffer[0 .. length] in newCounts)
     80  1.1  mrg     { // existing entry
     81  1.1  mrg         pcount.count++;
     82  1.1  mrg         pcount.size += sz;
     83  1.1  mrg     }
     84  1.1  mrg     else
     85  1.1  mrg     {
     86  1.1  mrg         auto key = (cast(char*) malloc(char.sizeof * length))[0 .. length];
     87  1.1  mrg         key[] = buffer[0..length];
     88  1.1  mrg         newCounts[key] = Entry(1, sz); // new entry
     89  1.1  mrg     }
     90  1.1  mrg }
     91  1.1  mrg 
     92  1.1  mrg // Merge thread local newCounts into globalNewCounts
     93  1.1  mrg static ~this()
     94  1.1  mrg {
     95  1.1  mrg     if (newCounts.length)
     96  1.1  mrg     {
     97  1.1  mrg         synchronized
     98  1.1  mrg         {
     99  1.1  mrg             foreach (name, entry; newCounts)
    100  1.1  mrg             {
    101  1.1  mrg                 if (!(name in globalNewCounts))
    102  1.1  mrg                     globalNewCounts[name] = Entry.init;
    103  1.1  mrg 
    104  1.1  mrg                 globalNewCounts[name].count += entry.count;
    105  1.1  mrg                 globalNewCounts[name].size += entry.size;
    106  1.1  mrg             }
    107  1.1  mrg         }
    108  1.1  mrg         newCounts.reset();
    109  1.1  mrg     }
    110  1.1  mrg     free(buffer.ptr);
    111  1.1  mrg     buffer = null;
    112  1.1  mrg }
    113  1.1  mrg 
    114  1.1  mrg // Write report to stderr
    115  1.1  mrg shared static ~this()
    116  1.1  mrg {
    117  1.1  mrg     static struct Result
    118  1.1  mrg     {
    119  1.1  mrg         const(char)[] name;
    120  1.1  mrg         Entry entry;
    121  1.1  mrg 
    122  1.1  mrg         // qsort() comparator to sort by count field
    123  1.1  mrg         extern (C) static int qsort_cmp(scope const void *r1, scope const void *r2) @nogc nothrow
    124  1.1  mrg         {
    125  1.1  mrg             auto result1 = cast(Result*)r1;
    126  1.1  mrg             auto result2 = cast(Result*)r2;
    127  1.1  mrg             long cmp = result2.entry.size - result1.entry.size;
    128  1.1  mrg             if (cmp) return cmp < 0 ? -1 : 1;
    129  1.1  mrg             cmp = result2.entry.count - result1.entry.count;
    130  1.1  mrg             if (cmp) return cmp < 0 ? -1 : 1;
    131  1.1  mrg             if (result2.name == result1.name) return 0;
    132  1.1  mrg             // ascending order for names reads better
    133  1.1  mrg             return result2.name > result1.name ? -1 : 1;
    134  1.1  mrg         }
    135  1.1  mrg     }
    136  1.1  mrg 
    137  1.1  mrg     size_t size = globalNewCounts.length;
    138  1.1  mrg     Result[] counts = (cast(Result*) malloc(size * Result.sizeof))[0 .. size];
    139  1.1  mrg     scope(exit)
    140  1.1  mrg         free(counts.ptr);
    141  1.1  mrg 
    142  1.1  mrg     size_t i;
    143  1.1  mrg     foreach (name, entry; globalNewCounts)
    144  1.1  mrg     {
    145  1.1  mrg         counts[i].name = name;
    146  1.1  mrg         counts[i].entry = entry;
    147  1.1  mrg         ++i;
    148  1.1  mrg     }
    149  1.1  mrg 
    150  1.1  mrg     if (counts.length)
    151  1.1  mrg     {
    152  1.1  mrg         qsort(counts.ptr, counts.length, Result.sizeof, &Result.qsort_cmp);
    153  1.1  mrg 
    154  1.1  mrg         FILE* fp = logfilename.length == 0 ? stdout : fopen((logfilename).ptr, "w");
    155  1.1  mrg         if (fp)
    156  1.1  mrg         {
    157  1.1  mrg             fprintf(fp, "bytes allocated, allocations, type, function, file:line\n");
    158  1.1  mrg             foreach (ref c; counts)
    159  1.1  mrg             {
    160  1.1  mrg                 fprintf(fp, "%15llu\t%15llu\t%8.*s\n",
    161  1.1  mrg                     cast(ulong)c.entry.size, cast(ulong)c.entry.count,
    162  1.1  mrg                     cast(int) c.name.length, c.name.ptr);
    163  1.1  mrg             }
    164  1.1  mrg             if (logfilename.length)
    165  1.1  mrg                 fclose(fp);
    166  1.1  mrg         }
    167  1.1  mrg         else
    168  1.1  mrg             fprintf(stderr, "cannot write profilegc log file '%.*s'", cast(int) logfilename.length, logfilename.ptr);
    169  1.1  mrg     }
    170  1.1  mrg }
    171