github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/packages/pyroscope-flamegraph/src/convert/convertJaegerTraceToProfile.ts (about) 1 /* eslint-disable import/prefer-default-export */ 2 import groupBy from 'lodash.groupby'; 3 import map from 'lodash.map'; 4 import type { Profile, Trace, TraceSpan } from '@pyroscope/models/src'; 5 import { deltaDiffWrapperReverse } from '../FlameGraph/decode'; 6 7 interface Span extends TraceSpan { 8 children: Span[]; 9 total: number; 10 self: number; 11 } 12 13 export function convertJaegerTraceToProfile(trace: Trace): Profile { 14 const resultFlamebearer = { 15 numTicks: 0, 16 maxSelf: 0, 17 names: [] as string[], 18 levels: [] as number[][], 19 }; 20 21 // Step 1: converting spans to a tree 22 23 const spans: Record<string, Span> = {}; 24 const root: Span = { children: [] } as unknown as Span; 25 (trace.spans as Span[]).forEach((span) => { 26 span.children = []; 27 spans[span.spanID] = span; 28 }); 29 30 (trace.spans as Span[]).forEach((span) => { 31 let node = root; 32 if (span.references && span.references.length > 0) { 33 node = spans[span.references[0].spanID] || root; 34 } 35 36 node.children.push(span); 37 }); 38 39 // Step 2: group spans with same name 40 41 function groupSpans(span: Span, d: number) { 42 (span.children || []).forEach((x) => groupSpans(x, d + 1)); 43 44 let childrenDur = 0; 45 const groups = groupBy(span.children || [], (x) => x.operationName); 46 span.children = map(groups, (group) => { 47 const res = group[0]; 48 for (let i = 1; i < group.length; i += 1) { 49 res.duration += group[i].duration; 50 } 51 childrenDur += res.duration; 52 return res; 53 }); 54 span.total = span.duration || childrenDur; 55 span.self = Math.max(0, span.total - childrenDur); 56 } 57 groupSpans(root, 0); 58 59 // Step 3: traversing the tree 60 61 function processNode(span: Span, level: number, offset: number) { 62 resultFlamebearer.numTicks ||= span.total; 63 resultFlamebearer.levels[level] ||= []; 64 resultFlamebearer.levels[level].push(offset); 65 resultFlamebearer.levels[level].push(span.total); 66 resultFlamebearer.levels[level].push(span.self); 67 resultFlamebearer.names.push( 68 (span.processID 69 ? `${trace.processes[span.processID].serviceName}: ` 70 : '') + (span.operationName || 'total') 71 ); 72 resultFlamebearer.levels[level].push(resultFlamebearer.names.length - 1); 73 74 (span.children || []).forEach((x) => { 75 offset += processNode(x, level + 1, offset); 76 }); 77 return span.total; 78 } 79 80 processNode(root, 0, 0); 81 82 resultFlamebearer.levels = deltaDiffWrapperReverse( 83 'single', 84 resultFlamebearer.levels 85 ); 86 87 return { 88 version: 1, 89 flamebearer: resultFlamebearer, 90 metadata: { 91 format: 'single', 92 units: 'trace_samples', 93 spyName: 'tracing', 94 sampleRate: 1000000, 95 }, 96 }; 97 }