#!/bin/awk -f

#**************************************************************************
#*                                                                        *
#*                                 OCaml                                  *
#*                                                                        *
#*                  Damien Doligez, Jane Street Group, LLC                *
#*                                                                        *
#*   Copyright 2014 Institut National de Recherche en Informatique et     *
#*     en Automatique.                                                    *
#*                                                                        *
#*   All rights reserved.  This file is distributed under the terms of    *
#*   the GNU Lesser General Public License version 2.1, with the          *
#*   special exception on linking described in the file LICENSE.          *
#*                                                                        *
#**************************************************************************

# usage:
#   ocaml-instr-report { file ... }
#   generate a report from the data files (or stdin if no file is given)

function short(n, kind,    i, r){
    for (i = 0; i < 5; i++){
        if (n < 1000) break;
        n /= 1000;
    }
    r = sprintf ("%f", n);
    if (index(r, ".") == 3){
        r = substr(r, 1, 2);
    }else{
        r = substr(r, 1, 3);
    }
    return sprintf("%s%s", r, units[kind,i]);
}

function add(limit){
    lim[nscales] = limit;
    scale["t",nscales] = short(limit, "t");
    scale["n",nscales] = short(limit, "n");
    ++ nscales;
}

# kind is "t" (for timer) or "n" (for number)
# events are simply a special kind of timer

BEGIN {
    units["t",0] = "ns";
    units["t",1] = "us";
    units["t",2] = "ms";
    units["t",3] = "s";
    units["t",4] = "ks";
    units["t",5] = "Ms";

    units["n",0] = "";
    units["n",1] = "k";
    units["n",2] = "M";
    units["n",3] = "G";
    units["n",4] = "T";
    units["n",5] = "P";

    nscales=0;
    add(0);
    for (mul = 100; mul < 10000000000; mul *= 10){
        add(mul);
        add(2.2 * mul);
        add(4.7 * mul);
    }
}

function store(value, tag) {
    ++ total[tag];
    for (i = 0; i < nscales; i++){
        if (value <= lim[i]){
            ++ bin[tag, lim[i]];
            val[tag, lim[i]] = value;
            return;
        }
    }
    ++ bin[tag, "off-scale"];
    val[tag, "off-scale"] = value;
}

$1 == "@@" && $4 ~ /@/ { total[$4] += $3; }

$1 == "@@" && $4 ~ /#/ { store($3, $4); }

$1 == "@@" { store($3 - $2, $4); }

function display(n, val, kind,    i) {
    graph_width = 35;

    if (n > 0){
        for (i = 0; i < log (n) / log (2); i++){
            printf("#");
        }
        if (n == 1){
            printf(" %-6d", n);
            printf ("%-*s", graph_width - 7 - i,
                    sprintf("(%s)", short(val, kind)));
        }else{
            printf(" %-*d", graph_width - 1 - i, n);
        }
    }else{
        printf("%*s", graph_width, "");
    }
}

END {
    n = asorti(total,tags);
    total_alloc = 0;
    for (i = 1; i <= n; i++){
        t = tags[i];
        if (t ~ /^alloc/) total_alloc += total[t];
    }
    for (i = 1; i <= n; i++){
        t = tags[i];
        if (t ~ /#/){
            kind = "n";        # number
        }else if (t ~ /@/){
            kind = "e";        # event
        }else{
            kind = "t";        # timer
        }
        if (kind == "e"){
            printf ("==== %-12s:%9d", t, total[t]);
            if (t ~ /^alloc/){
                cumul += total[t] / total_alloc;
                printf(" (%6.2f%%)", cumul * 100);
            }
            printf ("\n");
            continue;
        }else{
            printf ("==== %s: %d\n", t, total[t]);
        }
        num = bin[t,0];
        found = num;
        if (num == total[t] && kind == "t"){
            /* nothing */
        }else if (num > 0){
            printf ("           0: ");
            display(bin[t,0], val[t, 0], kind);
            printf ("%6.2f%%\n", found * 100 / total[t]);
        }
        for (j = 1; j < nscales; j++){
            if (found == total[t]) break;
            num = bin [t, lim[j]];
            found += num;
            if (found > 0){
                printf ("%5s..%-5s: ", scale[kind,j-1], scale[kind,j]);
                display(num, val[t, lim[j]], kind);
                printf ("%6.2f%%\n", found * 100 / total[t]);
            }
        }
        num = bin[t, "off-scale"];
        if (num != 0){
            printf ("  off scale : ");
            display(bin[t, "off-scale"], val[t, "off-scale"]);
            printf ("\n");
        }
        printf ("====\n");
    }
}
