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 }