github.com/grafana/pyroscope@v1.18.0/public/app/util/flamebearer.ts (about)

     1  import { DataFrameDTO, FieldType, createDataFrame } from '@grafana/data';
     2  
     3  export function deltaDiffWrapper(
     4    format: 'single' | 'double',
     5    levels: number[][]
     6  ) {
     7    const mutable_levels = [...levels];
     8  
     9    function deltaDiff(levels: number[][], start: number, step: number) {
    10      for (const level of levels) {
    11        let prev = 0;
    12        for (let i = start; i < level.length; i += step) {
    13          level[i] += prev;
    14          prev = level[i] + level[i + 1];
    15        }
    16      }
    17    }
    18  
    19    if (format === 'double') {
    20      deltaDiff(mutable_levels, 0, 7);
    21      deltaDiff(mutable_levels, 3, 7);
    22    } else {
    23      deltaDiff(mutable_levels, 0, 4);
    24    }
    25  
    26    return mutable_levels;
    27  }
    28  
    29  function getNodes(level: number[], names: string[], diff: boolean) {
    30    const nodes = [];
    31    const itemOffset = diff ? 7 : 4;
    32    for (let i = 0; i < level.length; i += itemOffset) {
    33      nodes.push({
    34        level: 0,
    35        label: diff ? names[level[i + 6]] : names[level[i + 3]],
    36        offset: level[i],
    37        val: level[i + 1],
    38        self: level[i + 2],
    39        selfRight: diff ? level[i + 5] : 0,
    40        valRight: diff ? level[i + 4] : 0,
    41        valTotal: diff ? level[i + 1] + level[i + 4] : level[i + 1],
    42        offsetRight: diff ? level[i + 3] : 0,
    43        offsetTotal: diff ? level[i] + level[i + 3] : level[i],
    44        children: [],
    45      });
    46    }
    47    return nodes;
    48  }
    49  
    50  export function flamebearerToDataFrameDTO(
    51    levels: number[][],
    52    names: string[],
    53    unit: string,
    54    diff: boolean
    55  ) {
    56    const nodeLevels: any[][] = [];
    57    for (let i = 0; i < levels.length; i++) {
    58      nodeLevels[i] = [];
    59      for (const node of getNodes(levels[i], names, diff)) {
    60        node.level = i;
    61        nodeLevels[i].push(node);
    62        if (i > 0) {
    63          const prevNodesInLevel = nodeLevels[i].slice(0, -1);
    64          const currentNodeStart =
    65            prevNodesInLevel.reduce(
    66              (acc, n) => n.offsetTotal + n.valTotal + acc,
    67              0
    68            ) + node.offsetTotal;
    69  
    70          const prevLevel = nodeLevels[i - 1];
    71          let prevLevelOffset = 0;
    72          for (const prevLevelNode of prevLevel) {
    73            const parentNodeStart = prevLevelOffset + prevLevelNode.offsetTotal;
    74            const parentNodeEnd = parentNodeStart + prevLevelNode.valTotal;
    75  
    76            if (
    77              parentNodeStart <= currentNodeStart &&
    78              parentNodeEnd > currentNodeStart
    79            ) {
    80              prevLevelNode.children.push(node);
    81              break;
    82            } else {
    83              prevLevelOffset +=
    84                prevLevelNode.offsetTotal + prevLevelNode.valTotal;
    85            }
    86          }
    87        }
    88      }
    89    }
    90  
    91    const root = nodeLevels[0][0];
    92    const stack = [root];
    93  
    94    const labelValues = [];
    95    const levelValues = [];
    96    const selfValues = [];
    97    const valueValues = [];
    98    const selfRightValues = [];
    99    const valueRightValues = [];
   100  
   101    while (stack.length) {
   102      const node = stack.shift();
   103      labelValues.push(node.label);
   104      levelValues.push(node.level);
   105      selfValues.push(node.self);
   106      valueValues.push(node.val);
   107      selfRightValues.push(node.selfRight);
   108      valueRightValues.push(node.valRight);
   109      stack.unshift(...node.children);
   110    }
   111  
   112    let valueUnit = 'short';
   113  
   114    // See format.ts#getFormatter. We have to use Grafana unit string here though.
   115    switch (unit) {
   116      case 'samples':
   117      case 'trace_samples':
   118      case 'lock_nanoseconds':
   119      case 'nanoseconds':
   120        valueUnit = 'ns';
   121        break;
   122      case 'bytes':
   123        valueUnit = 'bytes';
   124        break;
   125    }
   126  
   127    const fields = [
   128      { name: 'level', values: levelValues },
   129      { name: 'label', values: labelValues, type: FieldType.string },
   130      { name: 'self', values: selfValues, config: { unit: valueUnit } },
   131      { name: 'value', values: valueValues, config: { unit: valueUnit } },
   132    ];
   133  
   134    if (diff) {
   135      fields.push(
   136        ...[
   137          {
   138            name: 'selfRight',
   139            values: selfRightValues,
   140            config: { unit: valueUnit },
   141          },
   142          {
   143            name: 'valueRight',
   144            values: valueRightValues,
   145            config: { unit: valueUnit },
   146          },
   147        ]
   148      );
   149    }
   150  
   151    const frame: DataFrameDTO = {
   152      name: 'response',
   153      meta: { preferredVisualisationType: 'flamegraph' },
   154      fields,
   155    };
   156  
   157    return createDataFrame(frame);
   158  }