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