@grafana/flamegraph 11.5.0-221668 → 11.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -12
- package/dist/esm/FlameGraph/FlameGraph.js +36 -41
- package/dist/esm/FlameGraph/FlameGraph.js.map +1 -1
- package/dist/esm/FlameGraph/FlameGraphCanvas.js +12 -12
- package/dist/esm/FlameGraph/FlameGraphCanvas.js.map +1 -1
- package/dist/esm/FlameGraph/FlameGraphContextMenu.js +1 -1
- package/dist/esm/FlameGraph/FlameGraphContextMenu.js.map +1 -1
- package/dist/esm/FlameGraph/FlameGraphMetadata.js +10 -13
- package/dist/esm/FlameGraph/FlameGraphMetadata.js.map +1 -1
- package/dist/esm/FlameGraph/colors.js +2 -2
- package/dist/esm/FlameGraph/colors.js.map +1 -1
- package/dist/esm/FlameGraph/dataTransform.js +11 -11
- package/dist/esm/FlameGraph/dataTransform.js.map +1 -1
- package/dist/esm/FlameGraph/rendering.js +4 -4
- package/dist/esm/FlameGraph/rendering.js.map +1 -1
- package/dist/esm/FlameGraph/treeTransforms.js +4 -4
- package/dist/esm/FlameGraph/treeTransforms.js.map +1 -1
- package/dist/esm/FlameGraphContainer.js +12 -40
- package/dist/esm/FlameGraphContainer.js.map +1 -1
- package/dist/esm/TopTable/FlameGraphTopTableContainer.js +4 -4
- package/dist/esm/TopTable/FlameGraphTopTableContainer.js.map +1 -1
- package/dist/index.d.ts +1 -5
- package/dist/index.js +95 -131
- package/dist/index.js.map +1 -1
- package/package.json +11 -11
@@ -5,11 +5,11 @@ import { mergeParentSubtrees, mergeSubtrees } from './treeTransforms.js';
|
|
5
5
|
function nestedSetToLevels(container, options) {
|
6
6
|
const levels = [];
|
7
7
|
let offset = 0;
|
8
|
-
let parent =
|
8
|
+
let parent = void 0;
|
9
9
|
const uniqueLabels = {};
|
10
10
|
for (let i = 0; i < container.data.length; i++) {
|
11
11
|
const currentLevel = container.getLevel(i);
|
12
|
-
const prevLevel = i > 0 ? container.getLevel(i - 1) :
|
12
|
+
const prevLevel = i > 0 ? container.getLevel(i - 1) : void 0;
|
13
13
|
levels[currentLevel] = levels[currentLevel] || [];
|
14
14
|
if (prevLevel && prevLevel >= currentLevel) {
|
15
15
|
const lastSibling = levels[currentLevel][levels[currentLevel].length - 1];
|
@@ -19,7 +19,7 @@ function nestedSetToLevels(container, options) {
|
|
19
19
|
const newItem = {
|
20
20
|
itemIndexes: [i],
|
21
21
|
value: container.getValue(i) + container.getValueRight(i),
|
22
|
-
valueRight: container.isDiffFlamegraph() ? container.getValueRight(i) :
|
22
|
+
valueRight: container.isDiffFlamegraph() ? container.getValueRight(i) : void 0,
|
23
23
|
start: offset,
|
24
24
|
parents: parent && [parent],
|
25
25
|
children: [],
|
@@ -36,8 +36,8 @@ function nestedSetToLevels(container, options) {
|
|
36
36
|
parent = newItem;
|
37
37
|
levels[currentLevel].push(newItem);
|
38
38
|
}
|
39
|
-
const collapsedMapContainer = new CollapsedMapBuilder(options == null ?
|
40
|
-
if (options == null ?
|
39
|
+
const collapsedMapContainer = new CollapsedMapBuilder(options == null ? void 0 : options.collapsingThreshold);
|
40
|
+
if (options == null ? void 0 : options.collapsing) {
|
41
41
|
collapsedMapContainer.addTree(levels[0][0]);
|
42
42
|
}
|
43
43
|
return [levels, uniqueLabels, collapsedMapContainer.getCollapsedMap()];
|
@@ -84,7 +84,7 @@ class CollapsedMapBuilder {
|
|
84
84
|
constructor(threshold) {
|
85
85
|
this.map = /* @__PURE__ */ new Map();
|
86
86
|
this.threshold = 0.99;
|
87
|
-
if (threshold !==
|
87
|
+
if (threshold !== void 0) {
|
88
88
|
this.threshold = threshold;
|
89
89
|
}
|
90
90
|
}
|
@@ -93,7 +93,7 @@ class CollapsedMapBuilder {
|
|
93
93
|
const stack = [root];
|
94
94
|
while (stack.length) {
|
95
95
|
const current = stack.shift();
|
96
|
-
if ((_a = current.parents) == null ?
|
96
|
+
if ((_a = current.parents) == null ? void 0 : _a.length) {
|
97
97
|
this.addItem(current, current.parents[0]);
|
98
98
|
}
|
99
99
|
if (current.children.length) {
|
@@ -141,7 +141,7 @@ function checkFields(data) {
|
|
141
141
|
const wrongTypeFields = [];
|
142
142
|
for (const field of fields) {
|
143
143
|
const [name, types] = field;
|
144
|
-
const frameField = data == null ?
|
144
|
+
const frameField = data == null ? void 0 : data.fields.find((f) => f.name === name);
|
145
145
|
if (!frameField) {
|
146
146
|
missingFields.push(name);
|
147
147
|
continue;
|
@@ -156,7 +156,7 @@ function checkFields(data) {
|
|
156
156
|
missingFields
|
157
157
|
};
|
158
158
|
}
|
159
|
-
return
|
159
|
+
return void 0;
|
160
160
|
}
|
161
161
|
class FlameGraphDataContainer {
|
162
162
|
constructor(data, options, theme = createTheme()) {
|
@@ -178,7 +178,7 @@ class FlameGraphDataContainer {
|
|
178
178
|
"Malformed dataFrame: both valueRight and selfRight has to be present if one of them is present."
|
179
179
|
);
|
180
180
|
}
|
181
|
-
const enumConfig = (_c = (_b = (_a = this.labelField) == null ?
|
181
|
+
const enumConfig = (_c = (_b = (_a = this.labelField) == null ? void 0 : _a.config) == null ? void 0 : _b.type) == null ? void 0 : _c.enum;
|
182
182
|
if (enumConfig) {
|
183
183
|
this.labelDisplayProcessor = getDisplayProcessor({ field: this.labelField, theme });
|
184
184
|
this.uniqueLabels = enumConfig.text || [];
|
@@ -236,7 +236,7 @@ class FlameGraphDataContainer {
|
|
236
236
|
}
|
237
237
|
getSandwichLevels(label) {
|
238
238
|
const nodes = this.getNodesWithLabel(label);
|
239
|
-
if (!(nodes == null ?
|
239
|
+
if (!(nodes == null ? void 0 : nodes.length)) {
|
240
240
|
return [[], []];
|
241
241
|
}
|
242
242
|
const callers = mergeParentSubtrees(nodes, this);
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"dataTransform.js","sources":["../../../src/FlameGraph/dataTransform.ts"],"sourcesContent":["import {\n createTheme,\n DataFrame,\n DisplayProcessor,\n Field,\n FieldType,\n getDisplayProcessor,\n GrafanaTheme2,\n} from '@grafana/data';\n\nimport { SampleUnit } from '../types';\n\nimport { mergeParentSubtrees, mergeSubtrees } from './treeTransforms';\n\nexport type LevelItem = {\n // Offset from the start of the level.\n start: number;\n // Value here can be different from a value of items in the data frame as for callers tree in sandwich view we have\n // to trim the value to correspond only to the part used by the children in the subtree.\n // In case of diff profile this is actually left + right value.\n value: number;\n // Only exists for diff profiles.\n valueRight?: number;\n // Index into the data frame. It is an array because for sandwich views we may be merging multiple items into single\n // node.\n itemIndexes: number[];\n children: LevelItem[];\n level: number;\n parents?: LevelItem[];\n};\n\nexport type CollapseConfig = {\n items: LevelItem[];\n collapsed: boolean;\n};\n\n/**\n * Convert data frame with nested set format into array of level. This is mainly done for compatibility with current\n * rendering code.\n */\nexport function nestedSetToLevels(\n container: FlameGraphDataContainer,\n options?: Options\n): [LevelItem[][], Record<string, LevelItem[]>, CollapsedMap] {\n const levels: LevelItem[][] = [];\n let offset = 0;\n\n let parent: LevelItem | undefined = undefined;\n const uniqueLabels: Record<string, LevelItem[]> = {};\n\n for (let i = 0; i < container.data.length; i++) {\n const currentLevel = container.getLevel(i);\n const prevLevel = i > 0 ? container.getLevel(i - 1) : undefined;\n\n levels[currentLevel] = levels[currentLevel] || [];\n\n if (prevLevel && prevLevel >= currentLevel) {\n // We are going down a level or staying at the same level, so we are adding a sibling to the last item in a level.\n // So we have to compute the correct offset based on the last sibling.\n const lastSibling = levels[currentLevel][levels[currentLevel].length - 1];\n offset =\n lastSibling.start +\n container.getValue(lastSibling.itemIndexes[0]) +\n container.getValueRight(lastSibling.itemIndexes[0]);\n // we assume there is always a single root node so lastSibling should always have a parent.\n // Also it has to have the same parent because of how the items are ordered.\n parent = lastSibling.parents![0];\n }\n\n const newItem: LevelItem = {\n itemIndexes: [i],\n value: container.getValue(i) + container.getValueRight(i),\n valueRight: container.isDiffFlamegraph() ? container.getValueRight(i) : undefined,\n start: offset,\n parents: parent && [parent],\n children: [],\n level: currentLevel,\n };\n\n if (uniqueLabels[container.getLabel(i)]) {\n uniqueLabels[container.getLabel(i)].push(newItem);\n } else {\n uniqueLabels[container.getLabel(i)] = [newItem];\n }\n\n if (parent) {\n parent.children.push(newItem);\n }\n\n parent = newItem;\n levels[currentLevel].push(newItem);\n }\n\n const collapsedMapContainer = new CollapsedMapBuilder(options?.collapsingThreshold);\n if (options?.collapsing) {\n // We collapse similar items here, where it seems like parent and child are the same thing and so the distinction\n // isn't that important. We create a map of items that should be collapsed together. We need to do it with complete\n // tree as we need to know how many children an item has to know if we can collapse it.\n collapsedMapContainer.addTree(levels[0][0]);\n }\n\n return [levels, uniqueLabels, collapsedMapContainer.getCollapsedMap()];\n}\n\n/**\n * Small wrapper around the map of items that should be visually collapsed in the flame graph. Reason this is a wrapper\n * is that we want to make sure that when this is in the state we don't update the map directly but create a new map\n * and to have a place for the methods to collapse/expand either single item or all the items.\n */\nexport class CollapsedMap {\n // The levelItem used as a key is the item that will always be rendered in the flame graph. The config.items are all\n // the items that are in the group and if the config.collapsed is true they will be hidden.\n private map: Map<LevelItem, CollapseConfig> = new Map();\n\n constructor(map?: Map<LevelItem, CollapseConfig>) {\n this.map = map || new Map();\n }\n\n get(item: LevelItem) {\n return this.map.get(item);\n }\n\n keys() {\n return this.map.keys();\n }\n\n values() {\n return this.map.values();\n }\n\n size() {\n return this.map.size;\n }\n\n setCollapsedStatus(item: LevelItem, collapsed: boolean) {\n const newMap = new Map(this.map);\n const collapsedConfig = this.map.get(item)!;\n const newConfig = { ...collapsedConfig, collapsed };\n for (const item of collapsedConfig.items) {\n newMap.set(item, newConfig);\n }\n return new CollapsedMap(newMap);\n }\n\n setAllCollapsedStatus(collapsed: boolean) {\n const newMap = new Map(this.map);\n for (const item of this.map.keys()) {\n const collapsedConfig = this.map.get(item)!;\n const newConfig = { ...collapsedConfig, collapsed };\n newMap.set(item, newConfig);\n }\n\n return new CollapsedMap(newMap);\n }\n}\n\n/**\n * Similar to CollapsedMap but this one is mutable and used during transformation of the dataFrame data into structure\n * we use for rendering. This should not be passed to the React components.\n */\nexport class CollapsedMapBuilder {\n private map = new Map();\n private threshold = 0.99;\n\n constructor(threshold?: number) {\n if (threshold !== undefined) {\n this.threshold = threshold;\n }\n }\n\n addTree(root: LevelItem) {\n const stack = [root];\n while (stack.length) {\n const current = stack.shift()!;\n\n if (current.parents?.length) {\n this.addItem(current, current.parents[0]);\n }\n\n if (current.children.length) {\n stack.unshift(...current.children);\n }\n }\n }\n\n // The heuristics here is pretty simple right now. Just check if it's single child and if we are within threshold.\n // We assume items with small self just aren't too important while we cannot really collapse items with siblings\n // as it's not clear what to do with said sibling.\n addItem(item: LevelItem, parent?: LevelItem) {\n if (parent && item.value > parent.value * this.threshold && parent.children.length === 1) {\n if (this.map.has(parent)) {\n const config = this.map.get(parent)!;\n this.map.set(item, config);\n config.items.push(item);\n } else {\n const config = { items: [parent, item], collapsed: true };\n this.map.set(parent, config);\n this.map.set(item, config);\n }\n }\n }\n\n getCollapsedMap() {\n return new CollapsedMap(this.map);\n }\n}\n\nexport function getMessageCheckFieldsResult(wrongFields: CheckFieldsResult) {\n if (wrongFields.missingFields.length) {\n return `Data is missing fields: ${wrongFields.missingFields.join(', ')}`;\n }\n\n if (wrongFields.wrongTypeFields.length) {\n return `Data has fields of wrong type: ${wrongFields.wrongTypeFields\n .map((f) => `${f.name} has type ${f.type} but should be ${f.expectedTypes.join(' or ')}`)\n .join(', ')}`;\n }\n\n return '';\n}\n\nexport type CheckFieldsResult = {\n wrongTypeFields: Array<{ name: string; expectedTypes: FieldType[]; type: FieldType }>;\n missingFields: string[];\n};\n\nexport function checkFields(data: DataFrame): CheckFieldsResult | undefined {\n const fields: Array<[string, FieldType[]]> = [\n ['label', [FieldType.string, FieldType.enum]],\n ['level', [FieldType.number]],\n ['value', [FieldType.number]],\n ['self', [FieldType.number]],\n ];\n\n const missingFields = [];\n const wrongTypeFields = [];\n\n for (const field of fields) {\n const [name, types] = field;\n const frameField = data?.fields.find((f) => f.name === name);\n if (!frameField) {\n missingFields.push(name);\n continue;\n }\n if (!types.includes(frameField.type)) {\n wrongTypeFields.push({ name, expectedTypes: types, type: frameField.type });\n }\n }\n\n if (missingFields.length > 0 || wrongTypeFields.length > 0) {\n return {\n wrongTypeFields,\n missingFields,\n };\n }\n return undefined;\n}\n\nexport type Options = {\n collapsing: boolean;\n collapsingThreshold?: number;\n};\n\nexport class FlameGraphDataContainer {\n data: DataFrame;\n options: Options;\n\n labelField: Field;\n levelField: Field;\n valueField: Field;\n selfField: Field;\n\n // Optional fields for diff view\n valueRightField?: Field;\n selfRightField?: Field;\n\n labelDisplayProcessor: DisplayProcessor;\n valueDisplayProcessor: DisplayProcessor;\n uniqueLabels: string[];\n\n private levels: LevelItem[][] | undefined;\n private uniqueLabelsMap: Record<string, LevelItem[]> | undefined;\n private collapsedMap: CollapsedMap | undefined;\n\n constructor(data: DataFrame, options: Options, theme: GrafanaTheme2 = createTheme()) {\n this.data = data;\n this.options = options;\n\n const wrongFields = checkFields(data);\n if (wrongFields) {\n throw new Error(getMessageCheckFieldsResult(wrongFields));\n }\n\n this.labelField = data.fields.find((f) => f.name === 'label')!;\n this.levelField = data.fields.find((f) => f.name === 'level')!;\n this.valueField = data.fields.find((f) => f.name === 'value')!;\n this.selfField = data.fields.find((f) => f.name === 'self')!;\n\n this.valueRightField = data.fields.find((f) => f.name === 'valueRight')!;\n this.selfRightField = data.fields.find((f) => f.name === 'selfRight')!;\n\n if ((this.valueField || this.selfField) && !(this.valueField && this.selfField)) {\n throw new Error(\n 'Malformed dataFrame: both valueRight and selfRight has to be present if one of them is present.'\n );\n }\n\n const enumConfig = this.labelField?.config?.type?.enum;\n // Label can actually be an enum field so depending on that we have to access it through display processor. This is\n // both a backward compatibility but also to allow using a simple dataFrame without enum config. This would allow\n // users to use this panel with correct query from data sources that do not return profiles natively.\n if (enumConfig) {\n this.labelDisplayProcessor = getDisplayProcessor({ field: this.labelField, theme });\n this.uniqueLabels = enumConfig.text || [];\n } else {\n this.labelDisplayProcessor = (value) => ({\n text: value + '',\n numeric: 0,\n });\n this.uniqueLabels = [...new Set<string>(this.labelField.values)];\n }\n\n this.valueDisplayProcessor = getDisplayProcessor({\n field: this.valueField,\n theme,\n });\n }\n\n isDiffFlamegraph() {\n return Boolean(this.valueRightField && this.selfRightField);\n }\n\n getLabel(index: number) {\n return this.labelDisplayProcessor(this.labelField.values[index]).text;\n }\n\n getLevel(index: number) {\n return this.levelField.values[index];\n }\n\n getValue(index: number | number[]) {\n return fieldAccessor(this.valueField, index);\n }\n\n getValueRight(index: number | number[]) {\n return fieldAccessor(this.valueRightField, index);\n }\n\n getSelf(index: number | number[]) {\n return fieldAccessor(this.selfField, index);\n }\n\n getSelfRight(index: number | number[]) {\n return fieldAccessor(this.selfRightField, index);\n }\n\n getSelfDisplay(index: number | number[]) {\n return this.valueDisplayProcessor(this.getSelf(index));\n }\n\n getUniqueLabels() {\n return this.uniqueLabels;\n }\n\n getUnitTitle() {\n switch (this.valueField.config.unit) {\n case SampleUnit.Bytes:\n return 'RAM';\n case SampleUnit.Nanoseconds:\n return 'Time';\n }\n\n return 'Count';\n }\n\n getLevels() {\n this.initLevels();\n return this.levels!;\n }\n\n getSandwichLevels(label: string): [LevelItem[][], LevelItem[][]] {\n const nodes = this.getNodesWithLabel(label);\n\n if (!nodes?.length) {\n return [[], []];\n }\n\n const callers = mergeParentSubtrees(nodes, this);\n const callees = mergeSubtrees(nodes, this);\n\n return [callers, callees];\n }\n\n getNodesWithLabel(label: string) {\n this.initLevels();\n return this.uniqueLabelsMap![label];\n }\n\n getCollapsedMap() {\n this.initLevels();\n return this.collapsedMap!;\n }\n\n private initLevels() {\n if (!this.levels) {\n const [levels, uniqueLabelsMap, collapsedMap] = nestedSetToLevels(this, this.options);\n this.levels = levels;\n this.uniqueLabelsMap = uniqueLabelsMap;\n this.collapsedMap = collapsedMap;\n }\n }\n}\n\n// Access field value with either single index or array of indexes. This is needed as we sometimes merge multiple\n// into one, and we want to access aggregated values.\nfunction fieldAccessor(field: Field | undefined, index: number | number[]) {\n if (!field) {\n return 0;\n }\n let indexArray: number[] = typeof index === 'number' ? [index] : index;\n return indexArray.reduce((acc, index) => {\n return acc + field.values[index];\n }, 0);\n}\n"],"names":["item","index"],"mappings":"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"}
|
1
|
+
{"version":3,"file":"dataTransform.js","sources":["../../../src/FlameGraph/dataTransform.ts"],"sourcesContent":["import {\n createTheme,\n DataFrame,\n DisplayProcessor,\n Field,\n FieldType,\n getDisplayProcessor,\n GrafanaTheme2,\n} from '@grafana/data';\n\nimport { SampleUnit } from '../types';\n\nimport { mergeParentSubtrees, mergeSubtrees } from './treeTransforms';\n\nexport type LevelItem = {\n // Offset from the start of the level.\n start: number;\n // Value here can be different from a value of items in the data frame as for callers tree in sandwich view we have\n // to trim the value to correspond only to the part used by the children in the subtree.\n // In case of diff profile this is actually left + right value.\n value: number;\n // Only exists for diff profiles.\n valueRight?: number;\n // Index into the data frame. It is an array because for sandwich views we may be merging multiple items into single\n // node.\n itemIndexes: number[];\n children: LevelItem[];\n level: number;\n parents?: LevelItem[];\n};\n\nexport type CollapseConfig = {\n items: LevelItem[];\n collapsed: boolean;\n};\n\n/**\n * Convert data frame with nested set format into array of level. This is mainly done for compatibility with current\n * rendering code.\n */\nexport function nestedSetToLevels(\n container: FlameGraphDataContainer,\n options?: Options\n): [LevelItem[][], Record<string, LevelItem[]>, CollapsedMap] {\n const levels: LevelItem[][] = [];\n let offset = 0;\n\n let parent: LevelItem | undefined = undefined;\n const uniqueLabels: Record<string, LevelItem[]> = {};\n\n for (let i = 0; i < container.data.length; i++) {\n const currentLevel = container.getLevel(i);\n const prevLevel = i > 0 ? container.getLevel(i - 1) : undefined;\n\n levels[currentLevel] = levels[currentLevel] || [];\n\n if (prevLevel && prevLevel >= currentLevel) {\n // We are going down a level or staying at the same level, so we are adding a sibling to the last item in a level.\n // So we have to compute the correct offset based on the last sibling.\n const lastSibling = levels[currentLevel][levels[currentLevel].length - 1];\n offset =\n lastSibling.start +\n container.getValue(lastSibling.itemIndexes[0]) +\n container.getValueRight(lastSibling.itemIndexes[0]);\n // we assume there is always a single root node so lastSibling should always have a parent.\n // Also it has to have the same parent because of how the items are ordered.\n parent = lastSibling.parents![0];\n }\n\n const newItem: LevelItem = {\n itemIndexes: [i],\n value: container.getValue(i) + container.getValueRight(i),\n valueRight: container.isDiffFlamegraph() ? container.getValueRight(i) : undefined,\n start: offset,\n parents: parent && [parent],\n children: [],\n level: currentLevel,\n };\n\n if (uniqueLabels[container.getLabel(i)]) {\n uniqueLabels[container.getLabel(i)].push(newItem);\n } else {\n uniqueLabels[container.getLabel(i)] = [newItem];\n }\n\n if (parent) {\n parent.children.push(newItem);\n }\n\n parent = newItem;\n levels[currentLevel].push(newItem);\n }\n\n const collapsedMapContainer = new CollapsedMapBuilder(options?.collapsingThreshold);\n if (options?.collapsing) {\n // We collapse similar items here, where it seems like parent and child are the same thing and so the distinction\n // isn't that important. We create a map of items that should be collapsed together. We need to do it with complete\n // tree as we need to know how many children an item has to know if we can collapse it.\n collapsedMapContainer.addTree(levels[0][0]);\n }\n\n return [levels, uniqueLabels, collapsedMapContainer.getCollapsedMap()];\n}\n\n/**\n * Small wrapper around the map of items that should be visually collapsed in the flame graph. Reason this is a wrapper\n * is that we want to make sure that when this is in the state we don't update the map directly but create a new map\n * and to have a place for the methods to collapse/expand either single item or all the items.\n */\nexport class CollapsedMap {\n // The levelItem used as a key is the item that will always be rendered in the flame graph. The config.items are all\n // the items that are in the group and if the config.collapsed is true they will be hidden.\n private map: Map<LevelItem, CollapseConfig> = new Map();\n\n constructor(map?: Map<LevelItem, CollapseConfig>) {\n this.map = map || new Map();\n }\n\n get(item: LevelItem) {\n return this.map.get(item);\n }\n\n keys() {\n return this.map.keys();\n }\n\n values() {\n return this.map.values();\n }\n\n size() {\n return this.map.size;\n }\n\n setCollapsedStatus(item: LevelItem, collapsed: boolean) {\n const newMap = new Map(this.map);\n const collapsedConfig = this.map.get(item)!;\n const newConfig = { ...collapsedConfig, collapsed };\n for (const item of collapsedConfig.items) {\n newMap.set(item, newConfig);\n }\n return new CollapsedMap(newMap);\n }\n\n setAllCollapsedStatus(collapsed: boolean) {\n const newMap = new Map(this.map);\n for (const item of this.map.keys()) {\n const collapsedConfig = this.map.get(item)!;\n const newConfig = { ...collapsedConfig, collapsed };\n newMap.set(item, newConfig);\n }\n\n return new CollapsedMap(newMap);\n }\n}\n\n/**\n * Similar to CollapsedMap but this one is mutable and used during transformation of the dataFrame data into structure\n * we use for rendering. This should not be passed to the React components.\n */\nexport class CollapsedMapBuilder {\n private map = new Map();\n private threshold = 0.99;\n\n constructor(threshold?: number) {\n if (threshold !== undefined) {\n this.threshold = threshold;\n }\n }\n\n addTree(root: LevelItem) {\n const stack = [root];\n while (stack.length) {\n const current = stack.shift()!;\n\n if (current.parents?.length) {\n this.addItem(current, current.parents[0]);\n }\n\n if (current.children.length) {\n stack.unshift(...current.children);\n }\n }\n }\n\n // The heuristics here is pretty simple right now. Just check if it's single child and if we are within threshold.\n // We assume items with small self just aren't too important while we cannot really collapse items with siblings\n // as it's not clear what to do with said sibling.\n addItem(item: LevelItem, parent?: LevelItem) {\n if (parent && item.value > parent.value * this.threshold && parent.children.length === 1) {\n if (this.map.has(parent)) {\n const config = this.map.get(parent)!;\n this.map.set(item, config);\n config.items.push(item);\n } else {\n const config = { items: [parent, item], collapsed: true };\n this.map.set(parent, config);\n this.map.set(item, config);\n }\n }\n }\n\n getCollapsedMap() {\n return new CollapsedMap(this.map);\n }\n}\n\nexport function getMessageCheckFieldsResult(wrongFields: CheckFieldsResult) {\n if (wrongFields.missingFields.length) {\n return `Data is missing fields: ${wrongFields.missingFields.join(', ')}`;\n }\n\n if (wrongFields.wrongTypeFields.length) {\n return `Data has fields of wrong type: ${wrongFields.wrongTypeFields\n .map((f) => `${f.name} has type ${f.type} but should be ${f.expectedTypes.join(' or ')}`)\n .join(', ')}`;\n }\n\n return '';\n}\n\nexport type CheckFieldsResult = {\n wrongTypeFields: Array<{ name: string; expectedTypes: FieldType[]; type: FieldType }>;\n missingFields: string[];\n};\n\nexport function checkFields(data: DataFrame): CheckFieldsResult | undefined {\n const fields: Array<[string, FieldType[]]> = [\n ['label', [FieldType.string, FieldType.enum]],\n ['level', [FieldType.number]],\n ['value', [FieldType.number]],\n ['self', [FieldType.number]],\n ];\n\n const missingFields = [];\n const wrongTypeFields = [];\n\n for (const field of fields) {\n const [name, types] = field;\n const frameField = data?.fields.find((f) => f.name === name);\n if (!frameField) {\n missingFields.push(name);\n continue;\n }\n if (!types.includes(frameField.type)) {\n wrongTypeFields.push({ name, expectedTypes: types, type: frameField.type });\n }\n }\n\n if (missingFields.length > 0 || wrongTypeFields.length > 0) {\n return {\n wrongTypeFields,\n missingFields,\n };\n }\n return undefined;\n}\n\nexport type Options = {\n collapsing: boolean;\n collapsingThreshold?: number;\n};\n\nexport class FlameGraphDataContainer {\n data: DataFrame;\n options: Options;\n\n labelField: Field;\n levelField: Field;\n valueField: Field;\n selfField: Field;\n\n // Optional fields for diff view\n valueRightField?: Field;\n selfRightField?: Field;\n\n labelDisplayProcessor: DisplayProcessor;\n valueDisplayProcessor: DisplayProcessor;\n uniqueLabels: string[];\n\n private levels: LevelItem[][] | undefined;\n private uniqueLabelsMap: Record<string, LevelItem[]> | undefined;\n private collapsedMap: CollapsedMap | undefined;\n\n constructor(data: DataFrame, options: Options, theme: GrafanaTheme2 = createTheme()) {\n this.data = data;\n this.options = options;\n\n const wrongFields = checkFields(data);\n if (wrongFields) {\n throw new Error(getMessageCheckFieldsResult(wrongFields));\n }\n\n this.labelField = data.fields.find((f) => f.name === 'label')!;\n this.levelField = data.fields.find((f) => f.name === 'level')!;\n this.valueField = data.fields.find((f) => f.name === 'value')!;\n this.selfField = data.fields.find((f) => f.name === 'self')!;\n\n this.valueRightField = data.fields.find((f) => f.name === 'valueRight')!;\n this.selfRightField = data.fields.find((f) => f.name === 'selfRight')!;\n\n if ((this.valueField || this.selfField) && !(this.valueField && this.selfField)) {\n throw new Error(\n 'Malformed dataFrame: both valueRight and selfRight has to be present if one of them is present.'\n );\n }\n\n const enumConfig = this.labelField?.config?.type?.enum;\n // Label can actually be an enum field so depending on that we have to access it through display processor. This is\n // both a backward compatibility but also to allow using a simple dataFrame without enum config. This would allow\n // users to use this panel with correct query from data sources that do not return profiles natively.\n if (enumConfig) {\n this.labelDisplayProcessor = getDisplayProcessor({ field: this.labelField, theme });\n this.uniqueLabels = enumConfig.text || [];\n } else {\n this.labelDisplayProcessor = (value) => ({\n text: value + '',\n numeric: 0,\n });\n this.uniqueLabels = [...new Set<string>(this.labelField.values)];\n }\n\n this.valueDisplayProcessor = getDisplayProcessor({\n field: this.valueField,\n theme,\n });\n }\n\n isDiffFlamegraph() {\n return Boolean(this.valueRightField && this.selfRightField);\n }\n\n getLabel(index: number) {\n return this.labelDisplayProcessor(this.labelField.values[index]).text;\n }\n\n getLevel(index: number) {\n return this.levelField.values[index];\n }\n\n getValue(index: number | number[]) {\n return fieldAccessor(this.valueField, index);\n }\n\n getValueRight(index: number | number[]) {\n return fieldAccessor(this.valueRightField, index);\n }\n\n getSelf(index: number | number[]) {\n return fieldAccessor(this.selfField, index);\n }\n\n getSelfRight(index: number | number[]) {\n return fieldAccessor(this.selfRightField, index);\n }\n\n getSelfDisplay(index: number | number[]) {\n return this.valueDisplayProcessor(this.getSelf(index));\n }\n\n getUniqueLabels() {\n return this.uniqueLabels;\n }\n\n getUnitTitle() {\n switch (this.valueField.config.unit) {\n case SampleUnit.Bytes:\n return 'RAM';\n case SampleUnit.Nanoseconds:\n return 'Time';\n }\n\n return 'Count';\n }\n\n getLevels() {\n this.initLevels();\n return this.levels!;\n }\n\n getSandwichLevels(label: string): [LevelItem[][], LevelItem[][]] {\n const nodes = this.getNodesWithLabel(label);\n\n if (!nodes?.length) {\n return [[], []];\n }\n\n const callers = mergeParentSubtrees(nodes, this);\n const callees = mergeSubtrees(nodes, this);\n\n return [callers, callees];\n }\n\n getNodesWithLabel(label: string) {\n this.initLevels();\n return this.uniqueLabelsMap![label];\n }\n\n getCollapsedMap() {\n this.initLevels();\n return this.collapsedMap!;\n }\n\n private initLevels() {\n if (!this.levels) {\n const [levels, uniqueLabelsMap, collapsedMap] = nestedSetToLevels(this, this.options);\n this.levels = levels;\n this.uniqueLabelsMap = uniqueLabelsMap;\n this.collapsedMap = collapsedMap;\n }\n }\n}\n\n// Access field value with either single index or array of indexes. This is needed as we sometimes merge multiple\n// into one, and we want to access aggregated values.\nfunction fieldAccessor(field: Field | undefined, index: number | number[]) {\n if (!field) {\n return 0;\n }\n let indexArray: number[] = typeof index === 'number' ? [index] : index;\n return indexArray.reduce((acc, index) => {\n return acc + field.values[index];\n }, 0);\n}\n"],"names":["item","index"],"mappings":"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"}
|
@@ -144,7 +144,7 @@ function walkTree(root, direction, data, totalViewTicks, rangeMin, rangeMax, wra
|
|
144
144
|
const stack = [];
|
145
145
|
stack.push({ item: root, levelOffset: 0 });
|
146
146
|
const pixelsPerTick = wrapperWidth * window.devicePixelRatio / totalViewTicks / (rangeMax - rangeMin);
|
147
|
-
let collapsedItemRendered =
|
147
|
+
let collapsedItemRendered = void 0;
|
148
148
|
while (stack.length > 0) {
|
149
149
|
const { item, levelOffset } = stack.shift();
|
150
150
|
let curBarTicks = item.value;
|
@@ -163,10 +163,10 @@ function walkTree(root, direction, data, totalViewTicks, rangeMin, rangeMax, wra
|
|
163
163
|
offsetModifier = direction === "children" ? -1 : 1;
|
164
164
|
skipRender = true;
|
165
165
|
} else {
|
166
|
-
collapsedItemRendered =
|
166
|
+
collapsedItemRendered = void 0;
|
167
167
|
}
|
168
168
|
} else {
|
169
|
-
collapsedItemRendered =
|
169
|
+
collapsedItemRendered = void 0;
|
170
170
|
}
|
171
171
|
if (!skipRender) {
|
172
172
|
const barX = getBarX(item.start, totalViewTicks, rangeMin, pixelsPerTick);
|
@@ -189,7 +189,7 @@ function useColorFunction(totalTicks, totalTicksRight, colorScheme, theme, muted
|
|
189
189
|
if (muted && !matchedLabels) {
|
190
190
|
return mutedColor;
|
191
191
|
}
|
192
|
-
const barColor = item.valueRight !==
|
192
|
+
const barColor = item.valueRight !== void 0 && (colorScheme === ColorSchemeDiff.Default || colorScheme === ColorSchemeDiff.DiffColorBlind) ? getBarColorByDiff(item.value, item.valueRight, totalTicks, totalTicksRight, colorScheme) : colorScheme === ColorScheme.ValueBased ? getBarColorByValue(item.value, totalTicks, rangeMin, rangeMax) : getBarColorByPackage(label, theme);
|
193
193
|
if (matchedLabels) {
|
194
194
|
return matchedLabels.has(label) ? barColor.toHslString() : mutedColor;
|
195
195
|
}
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"rendering.js","sources":["../../../src/FlameGraph/rendering.ts"],"sourcesContent":["import { RefObject, useCallback, useEffect, useMemo, useState } from 'react';\nimport color from 'tinycolor2';\n\nimport { GrafanaTheme2 } from '@grafana/data';\nimport { useTheme2 } from '@grafana/ui';\n\nimport {\n BAR_BORDER_WIDTH,\n BAR_TEXT_PADDING_LEFT,\n MUTE_THRESHOLD,\n HIDE_THRESHOLD,\n LABEL_THRESHOLD,\n PIXELS_PER_LEVEL,\n GROUP_STRIP_WIDTH,\n GROUP_STRIP_PADDING,\n GROUP_STRIP_MARGIN_LEFT,\n GROUP_TEXT_OFFSET,\n} from '../constants';\nimport { ClickedItemData, ColorScheme, ColorSchemeDiff, TextAlign } from '../types';\n\nimport { getBarColorByDiff, getBarColorByPackage, getBarColorByValue } from './colors';\nimport { CollapseConfig, CollapsedMap, FlameGraphDataContainer, LevelItem } from './dataTransform';\n\ntype RenderOptions = {\n canvasRef: RefObject<HTMLCanvasElement>;\n data: FlameGraphDataContainer;\n root: LevelItem;\n direction: 'children' | 'parents';\n\n // Depth in number of levels\n depth: number;\n wrapperWidth: number;\n\n // If we are rendering only zoomed in part of the graph.\n rangeMin: number;\n rangeMax: number;\n\n matchedLabels: Set<string> | undefined;\n textAlign: TextAlign;\n\n // Total ticks that will be used for sizing\n totalViewTicks: number;\n // Total ticks that will be used for computing colors as some color scheme (like in diff view) should not be affected\n // by sandwich or focus view.\n totalColorTicks: number;\n // Total ticks used to compute the diff colors\n totalTicksRight: number | undefined;\n colorScheme: ColorScheme | ColorSchemeDiff;\n focusedItemData?: ClickedItemData;\n collapsedMap: CollapsedMap;\n};\n\nexport function useFlameRender(options: RenderOptions) {\n const {\n canvasRef,\n data,\n root,\n depth,\n direction,\n wrapperWidth,\n rangeMin,\n rangeMax,\n matchedLabels,\n textAlign,\n totalViewTicks,\n totalColorTicks,\n totalTicksRight,\n colorScheme,\n focusedItemData,\n collapsedMap,\n } = options;\n const ctx = useSetupCanvas(canvasRef, wrapperWidth, depth);\n const theme = useTheme2();\n\n // There is a bit of dependency injections here that does not add readability, mainly to prevent recomputing some\n // common stuff for all the nodes in the graph when only once is enough. perf/readability tradeoff.\n\n const mutedColor = useMemo(() => {\n const barMutedColor = color(theme.colors.background.secondary);\n return theme.isLight ? barMutedColor.darken(10).toHexString() : barMutedColor.lighten(10).toHexString();\n }, [theme]);\n\n const getBarColor = useColorFunction(\n totalColorTicks,\n totalTicksRight,\n colorScheme,\n theme,\n mutedColor,\n rangeMin,\n rangeMax,\n matchedLabels,\n focusedItemData ? focusedItemData.item.level : 0\n );\n\n const renderFunc = useRenderFunc(ctx, data, getBarColor, textAlign, collapsedMap);\n\n useEffect(() => {\n if (!ctx) {\n return;\n }\n\n ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);\n\n const mutedPath2D = new Path2D();\n\n //\n // Walk the tree and compute the dimensions for each item in the flamegraph.\n //\n walkTree(\n root,\n direction,\n data,\n totalViewTicks,\n rangeMin,\n rangeMax,\n wrapperWidth,\n collapsedMap,\n (item, x, y, width, height, label, muted) => {\n if (muted) {\n // We do a bit of optimization for muted regions, and we render them all in single fill later on as they don't\n // have labels and are the same color.\n mutedPath2D.rect(x, y, width, height);\n } else {\n renderFunc(item, x, y, width, height, label);\n }\n }\n );\n\n // Only fill the muted rects\n ctx.fillStyle = mutedColor;\n ctx.fill(mutedPath2D);\n }, [\n ctx,\n data,\n root,\n wrapperWidth,\n rangeMin,\n rangeMax,\n totalViewTicks,\n direction,\n renderFunc,\n collapsedMap,\n mutedColor,\n ]);\n}\n\ntype RenderFunc = (item: LevelItem, x: number, y: number, width: number, height: number, label: string) => void;\n\ntype RenderFuncWrap = (\n item: LevelItem,\n x: number,\n y: number,\n width: number,\n height: number,\n label: string,\n muted: boolean\n) => void;\n\n/**\n * Create a render function with some memoization to prevent excesive repainting of the canvas.\n * @param ctx\n * @param data\n * @param getBarColor\n * @param textAlign\n * @param collapsedMap\n */\nfunction useRenderFunc(\n ctx: CanvasRenderingContext2D | undefined,\n data: FlameGraphDataContainer,\n getBarColor: (item: LevelItem, label: string, muted: boolean) => string,\n textAlign: TextAlign,\n collapsedMap: CollapsedMap\n) {\n return useMemo(() => {\n if (!ctx) {\n return () => {};\n }\n\n const renderFunc: RenderFunc = (item, x, y, width, height, label) => {\n ctx.beginPath();\n ctx.rect(x + BAR_BORDER_WIDTH, y, width, height);\n ctx.fillStyle = getBarColor(item, label, false);\n ctx.stroke();\n ctx.fill();\n\n const collapsedItemConfig = collapsedMap.get(item);\n let finalLabel = label;\n if (collapsedItemConfig && collapsedItemConfig.collapsed) {\n const numberOfCollapsedItems = collapsedItemConfig.items.length;\n finalLabel = `(${numberOfCollapsedItems}) ` + label;\n }\n\n if (width >= LABEL_THRESHOLD) {\n if (collapsedItemConfig) {\n renderLabel(\n ctx,\n data,\n finalLabel,\n item,\n width,\n textAlign === 'left' ? x + GROUP_STRIP_MARGIN_LEFT + GROUP_TEXT_OFFSET : x,\n y,\n textAlign\n );\n\n renderGroupingStrip(ctx, x, y, height, item, collapsedItemConfig);\n } else {\n renderLabel(ctx, data, finalLabel, item, width, x, y, textAlign);\n }\n }\n };\n\n return renderFunc;\n }, [ctx, getBarColor, textAlign, data, collapsedMap]);\n}\n\n/**\n * Render small strip on the left side of the bar to indicate that this item is part of a group that can be collapsed.\n * @param ctx\n * @param x\n * @param y\n * @param height\n * @param item\n * @param collapsedItemConfig\n */\nfunction renderGroupingStrip(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n height: number,\n item: LevelItem,\n collapsedItemConfig: CollapseConfig\n) {\n const groupStripX = x + GROUP_STRIP_MARGIN_LEFT;\n\n // This is to mask the label in case we align it right to left.\n ctx.beginPath();\n ctx.rect(x, y, groupStripX - x + GROUP_STRIP_WIDTH + GROUP_STRIP_PADDING, height);\n ctx.fill();\n\n // For item in a group that can be collapsed, we draw a small strip to mark them. On the items that are at the\n // start or and end of a group we draw just half the strip so 2 groups next to each other are separated\n // visually.\n ctx.beginPath();\n if (collapsedItemConfig.collapsed) {\n ctx.rect(groupStripX, y + height / 4, GROUP_STRIP_WIDTH, height / 2);\n } else {\n if (collapsedItemConfig.items[0] === item) {\n // Top item\n ctx.rect(groupStripX, y + height / 2, GROUP_STRIP_WIDTH, height / 2);\n } else if (collapsedItemConfig.items[collapsedItemConfig.items.length - 1] === item) {\n // Bottom item\n ctx.rect(groupStripX, y, GROUP_STRIP_WIDTH, height / 2);\n } else {\n ctx.rect(groupStripX, y, GROUP_STRIP_WIDTH, height);\n }\n }\n\n ctx.fillStyle = '#666';\n ctx.fill();\n}\n\n/**\n * Exported for testing don't use directly\n * Walks the tree and computes coordinates, dimensions and other data needed for rendering. For each item in the tree\n * it defers the rendering to the renderFunc.\n */\nexport function walkTree(\n root: LevelItem,\n // In sandwich view we use parents direction to show all callers.\n direction: 'children' | 'parents',\n data: FlameGraphDataContainer,\n totalViewTicks: number,\n rangeMin: number,\n rangeMax: number,\n wrapperWidth: number,\n collapsedMap: CollapsedMap,\n renderFunc: RenderFuncWrap\n) {\n // The levelOffset here is to keep track if items that we don't render because they are collapsed into single row.\n // That means we have to render next items with an offset of some rows up in the stack.\n const stack: Array<{ item: LevelItem; levelOffset: number }> = [];\n stack.push({ item: root, levelOffset: 0 });\n\n const pixelsPerTick = (wrapperWidth * window.devicePixelRatio) / totalViewTicks / (rangeMax - rangeMin);\n let collapsedItemRendered: LevelItem | undefined = undefined;\n\n while (stack.length > 0) {\n const { item, levelOffset } = stack.shift()!;\n let curBarTicks = item.value;\n const muted = curBarTicks * pixelsPerTick <= MUTE_THRESHOLD;\n const width = curBarTicks * pixelsPerTick - (muted ? 0 : BAR_BORDER_WIDTH * 2);\n const height = PIXELS_PER_LEVEL;\n\n if (width < HIDE_THRESHOLD) {\n // We don't render nor it's children\n continue;\n }\n\n let offsetModifier = 0;\n let skipRender = false;\n const collapsedItemConfig = collapsedMap.get(item);\n const isCollapsedItem = collapsedItemConfig && collapsedItemConfig.collapsed;\n\n if (isCollapsedItem) {\n if (collapsedItemRendered === collapsedItemConfig.items[0]) {\n offsetModifier = direction === 'children' ? -1 : +1;\n skipRender = true;\n } else {\n // This is a case where we have another collapsed group right after different collapsed group, so we need to\n // reset.\n collapsedItemRendered = undefined;\n }\n } else {\n collapsedItemRendered = undefined;\n }\n\n if (!skipRender) {\n const barX = getBarX(item.start, totalViewTicks, rangeMin, pixelsPerTick);\n const barY = (item.level + levelOffset) * PIXELS_PER_LEVEL;\n\n let label = data.getLabel(item.itemIndexes[0]);\n if (isCollapsedItem) {\n collapsedItemRendered = item;\n }\n\n renderFunc(item, barX, barY, width, height, label, muted);\n }\n\n const nextList = direction === 'children' ? item.children : item.parents;\n if (nextList) {\n stack.unshift(...nextList.map((c) => ({ item: c, levelOffset: levelOffset + offsetModifier })));\n }\n }\n}\n\nfunction useColorFunction(\n totalTicks: number,\n totalTicksRight: number | undefined,\n colorScheme: ColorScheme | ColorSchemeDiff,\n theme: GrafanaTheme2,\n mutedColor: string,\n rangeMin: number,\n rangeMax: number,\n matchedLabels: Set<string> | undefined,\n topLevel: number\n) {\n return useCallback(\n function getColor(item: LevelItem, label: string, muted: boolean) {\n // If collapsed and no search we can quickly return the muted color\n if (muted && !matchedLabels) {\n // Collapsed are always grayed\n return mutedColor;\n }\n\n const barColor =\n item.valueRight !== undefined &&\n (colorScheme === ColorSchemeDiff.Default || colorScheme === ColorSchemeDiff.DiffColorBlind)\n ? getBarColorByDiff(item.value, item.valueRight!, totalTicks, totalTicksRight!, colorScheme)\n : colorScheme === ColorScheme.ValueBased\n ? getBarColorByValue(item.value, totalTicks, rangeMin, rangeMax)\n : getBarColorByPackage(label, theme);\n\n if (matchedLabels) {\n // Means we are searching, we use color for matches and gray the rest\n return matchedLabels.has(label) ? barColor.toHslString() : mutedColor;\n }\n\n // Mute if we are above the focused symbol\n return item.level > topLevel - 1 ? barColor.toHslString() : barColor.lighten(15).toHslString();\n },\n [totalTicks, totalTicksRight, colorScheme, theme, rangeMin, rangeMax, matchedLabels, topLevel, mutedColor]\n );\n}\n\nfunction useSetupCanvas(canvasRef: RefObject<HTMLCanvasElement>, wrapperWidth: number, numberOfLevels: number) {\n const [ctx, setCtx] = useState<CanvasRenderingContext2D>();\n\n useEffect(() => {\n if (!(numberOfLevels && canvasRef.current)) {\n return;\n }\n const ctx = canvasRef.current.getContext('2d')!;\n\n const height = PIXELS_PER_LEVEL * numberOfLevels;\n canvasRef.current.width = Math.round(wrapperWidth * window.devicePixelRatio);\n canvasRef.current.height = Math.round(height);\n canvasRef.current.style.width = `${wrapperWidth}px`;\n canvasRef.current.style.height = `${height / window.devicePixelRatio}px`;\n\n ctx.textBaseline = 'middle';\n ctx.font = 12 * window.devicePixelRatio + 'px monospace';\n ctx.strokeStyle = 'white';\n setCtx(ctx);\n }, [canvasRef, setCtx, wrapperWidth, numberOfLevels]);\n return ctx;\n}\n\n// Renders a text inside the node rectangle. It allows setting alignment of the text left or right which takes effect\n// when text is too long to fit in the rectangle.\nfunction renderLabel(\n ctx: CanvasRenderingContext2D,\n data: FlameGraphDataContainer,\n label: string,\n item: LevelItem,\n width: number,\n x: number,\n y: number,\n textAlign: TextAlign\n) {\n ctx.save();\n ctx.clip(); // so text does not overflow\n ctx.fillStyle = '#222';\n\n const displayValue = data.valueDisplayProcessor(item.value);\n const unit = displayValue.suffix ? displayValue.text + displayValue.suffix : displayValue.text;\n\n // We only measure name here instead of full label because of how we deal with the units and aligning later.\n const measure = ctx.measureText(label);\n const spaceForTextInRect = width - BAR_TEXT_PADDING_LEFT;\n\n let fullLabel = `${label} (${unit})`;\n let labelX = Math.max(x, 0) + BAR_TEXT_PADDING_LEFT;\n\n // We use the desired alignment only if there is not enough space for the text, otherwise we keep left alignment as\n // that will already show full text.\n if (measure.width > spaceForTextInRect) {\n ctx.textAlign = textAlign;\n // If aligned to the right we don't want to take the space with the unit label as the assumption is user wants to\n // mainly see the name. This also reflects how pyro/flamegraph works.\n if (textAlign === 'right') {\n fullLabel = label;\n labelX = x + width - BAR_TEXT_PADDING_LEFT;\n }\n }\n\n ctx.fillText(fullLabel, labelX, y + PIXELS_PER_LEVEL / 2 + 2);\n ctx.restore();\n}\n\n/**\n * Returns the X position of the bar. totalTicks * rangeMin is to adjust for any current zoom. So if we zoom to a\n * section of the graph we align and shift the X coordinates accordingly.\n * @param offset\n * @param totalTicks\n * @param rangeMin\n * @param pixelsPerTick\n */\nexport function getBarX(offset: number, totalTicks: number, rangeMin: number, pixelsPerTick: number) {\n return (offset - totalTicks * rangeMin) * pixelsPerTick;\n}\n"],"names":["ctx"],"mappings":"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"}
|
1
|
+
{"version":3,"file":"rendering.js","sources":["../../../src/FlameGraph/rendering.ts"],"sourcesContent":["import { RefObject, useCallback, useEffect, useMemo, useState } from 'react';\nimport color from 'tinycolor2';\n\nimport { GrafanaTheme2 } from '@grafana/data';\nimport { useTheme2 } from '@grafana/ui';\n\nimport {\n BAR_BORDER_WIDTH,\n BAR_TEXT_PADDING_LEFT,\n MUTE_THRESHOLD,\n HIDE_THRESHOLD,\n LABEL_THRESHOLD,\n PIXELS_PER_LEVEL,\n GROUP_STRIP_WIDTH,\n GROUP_STRIP_PADDING,\n GROUP_STRIP_MARGIN_LEFT,\n GROUP_TEXT_OFFSET,\n} from '../constants';\nimport { ClickedItemData, ColorScheme, ColorSchemeDiff, TextAlign } from '../types';\n\nimport { getBarColorByDiff, getBarColorByPackage, getBarColorByValue } from './colors';\nimport { CollapseConfig, CollapsedMap, FlameGraphDataContainer, LevelItem } from './dataTransform';\n\ntype RenderOptions = {\n canvasRef: RefObject<HTMLCanvasElement>;\n data: FlameGraphDataContainer;\n root: LevelItem;\n direction: 'children' | 'parents';\n\n // Depth in number of levels\n depth: number;\n wrapperWidth: number;\n\n // If we are rendering only zoomed in part of the graph.\n rangeMin: number;\n rangeMax: number;\n\n matchedLabels: Set<string> | undefined;\n textAlign: TextAlign;\n\n // Total ticks that will be used for sizing\n totalViewTicks: number;\n // Total ticks that will be used for computing colors as some color scheme (like in diff view) should not be affected\n // by sandwich or focus view.\n totalColorTicks: number;\n // Total ticks used to compute the diff colors\n totalTicksRight: number | undefined;\n colorScheme: ColorScheme | ColorSchemeDiff;\n focusedItemData?: ClickedItemData;\n collapsedMap: CollapsedMap;\n};\n\nexport function useFlameRender(options: RenderOptions) {\n const {\n canvasRef,\n data,\n root,\n depth,\n direction,\n wrapperWidth,\n rangeMin,\n rangeMax,\n matchedLabels,\n textAlign,\n totalViewTicks,\n totalColorTicks,\n totalTicksRight,\n colorScheme,\n focusedItemData,\n collapsedMap,\n } = options;\n const ctx = useSetupCanvas(canvasRef, wrapperWidth, depth);\n const theme = useTheme2();\n\n // There is a bit of dependency injections here that does not add readability, mainly to prevent recomputing some\n // common stuff for all the nodes in the graph when only once is enough. perf/readability tradeoff.\n\n const mutedColor = useMemo(() => {\n const barMutedColor = color(theme.colors.background.secondary);\n return theme.isLight ? barMutedColor.darken(10).toHexString() : barMutedColor.lighten(10).toHexString();\n }, [theme]);\n\n const getBarColor = useColorFunction(\n totalColorTicks,\n totalTicksRight,\n colorScheme,\n theme,\n mutedColor,\n rangeMin,\n rangeMax,\n matchedLabels,\n focusedItemData ? focusedItemData.item.level : 0\n );\n\n const renderFunc = useRenderFunc(ctx, data, getBarColor, textAlign, collapsedMap);\n\n useEffect(() => {\n if (!ctx) {\n return;\n }\n\n ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);\n\n const mutedPath2D = new Path2D();\n\n //\n // Walk the tree and compute the dimensions for each item in the flamegraph.\n //\n walkTree(\n root,\n direction,\n data,\n totalViewTicks,\n rangeMin,\n rangeMax,\n wrapperWidth,\n collapsedMap,\n (item, x, y, width, height, label, muted) => {\n if (muted) {\n // We do a bit of optimization for muted regions, and we render them all in single fill later on as they don't\n // have labels and are the same color.\n mutedPath2D.rect(x, y, width, height);\n } else {\n renderFunc(item, x, y, width, height, label);\n }\n }\n );\n\n // Only fill the muted rects\n ctx.fillStyle = mutedColor;\n ctx.fill(mutedPath2D);\n }, [\n ctx,\n data,\n root,\n wrapperWidth,\n rangeMin,\n rangeMax,\n totalViewTicks,\n direction,\n renderFunc,\n collapsedMap,\n mutedColor,\n ]);\n}\n\ntype RenderFunc = (item: LevelItem, x: number, y: number, width: number, height: number, label: string) => void;\n\ntype RenderFuncWrap = (\n item: LevelItem,\n x: number,\n y: number,\n width: number,\n height: number,\n label: string,\n muted: boolean\n) => void;\n\n/**\n * Create a render function with some memoization to prevent excesive repainting of the canvas.\n * @param ctx\n * @param data\n * @param getBarColor\n * @param textAlign\n * @param collapsedMap\n */\nfunction useRenderFunc(\n ctx: CanvasRenderingContext2D | undefined,\n data: FlameGraphDataContainer,\n getBarColor: (item: LevelItem, label: string, muted: boolean) => string,\n textAlign: TextAlign,\n collapsedMap: CollapsedMap\n) {\n return useMemo(() => {\n if (!ctx) {\n return () => {};\n }\n\n const renderFunc: RenderFunc = (item, x, y, width, height, label) => {\n ctx.beginPath();\n ctx.rect(x + BAR_BORDER_WIDTH, y, width, height);\n ctx.fillStyle = getBarColor(item, label, false);\n ctx.stroke();\n ctx.fill();\n\n const collapsedItemConfig = collapsedMap.get(item);\n let finalLabel = label;\n if (collapsedItemConfig && collapsedItemConfig.collapsed) {\n const numberOfCollapsedItems = collapsedItemConfig.items.length;\n finalLabel = `(${numberOfCollapsedItems}) ` + label;\n }\n\n if (width >= LABEL_THRESHOLD) {\n if (collapsedItemConfig) {\n renderLabel(\n ctx,\n data,\n finalLabel,\n item,\n width,\n textAlign === 'left' ? x + GROUP_STRIP_MARGIN_LEFT + GROUP_TEXT_OFFSET : x,\n y,\n textAlign\n );\n\n renderGroupingStrip(ctx, x, y, height, item, collapsedItemConfig);\n } else {\n renderLabel(ctx, data, finalLabel, item, width, x, y, textAlign);\n }\n }\n };\n\n return renderFunc;\n }, [ctx, getBarColor, textAlign, data, collapsedMap]);\n}\n\n/**\n * Render small strip on the left side of the bar to indicate that this item is part of a group that can be collapsed.\n * @param ctx\n * @param x\n * @param y\n * @param height\n * @param item\n * @param collapsedItemConfig\n */\nfunction renderGroupingStrip(\n ctx: CanvasRenderingContext2D,\n x: number,\n y: number,\n height: number,\n item: LevelItem,\n collapsedItemConfig: CollapseConfig\n) {\n const groupStripX = x + GROUP_STRIP_MARGIN_LEFT;\n\n // This is to mask the label in case we align it right to left.\n ctx.beginPath();\n ctx.rect(x, y, groupStripX - x + GROUP_STRIP_WIDTH + GROUP_STRIP_PADDING, height);\n ctx.fill();\n\n // For item in a group that can be collapsed, we draw a small strip to mark them. On the items that are at the\n // start or and end of a group we draw just half the strip so 2 groups next to each other are separated\n // visually.\n ctx.beginPath();\n if (collapsedItemConfig.collapsed) {\n ctx.rect(groupStripX, y + height / 4, GROUP_STRIP_WIDTH, height / 2);\n } else {\n if (collapsedItemConfig.items[0] === item) {\n // Top item\n ctx.rect(groupStripX, y + height / 2, GROUP_STRIP_WIDTH, height / 2);\n } else if (collapsedItemConfig.items[collapsedItemConfig.items.length - 1] === item) {\n // Bottom item\n ctx.rect(groupStripX, y, GROUP_STRIP_WIDTH, height / 2);\n } else {\n ctx.rect(groupStripX, y, GROUP_STRIP_WIDTH, height);\n }\n }\n\n ctx.fillStyle = '#666';\n ctx.fill();\n}\n\n/**\n * Exported for testing don't use directly\n * Walks the tree and computes coordinates, dimensions and other data needed for rendering. For each item in the tree\n * it defers the rendering to the renderFunc.\n */\nexport function walkTree(\n root: LevelItem,\n // In sandwich view we use parents direction to show all callers.\n direction: 'children' | 'parents',\n data: FlameGraphDataContainer,\n totalViewTicks: number,\n rangeMin: number,\n rangeMax: number,\n wrapperWidth: number,\n collapsedMap: CollapsedMap,\n renderFunc: RenderFuncWrap\n) {\n // The levelOffset here is to keep track if items that we don't render because they are collapsed into single row.\n // That means we have to render next items with an offset of some rows up in the stack.\n const stack: Array<{ item: LevelItem; levelOffset: number }> = [];\n stack.push({ item: root, levelOffset: 0 });\n\n const pixelsPerTick = (wrapperWidth * window.devicePixelRatio) / totalViewTicks / (rangeMax - rangeMin);\n let collapsedItemRendered: LevelItem | undefined = undefined;\n\n while (stack.length > 0) {\n const { item, levelOffset } = stack.shift()!;\n let curBarTicks = item.value;\n const muted = curBarTicks * pixelsPerTick <= MUTE_THRESHOLD;\n const width = curBarTicks * pixelsPerTick - (muted ? 0 : BAR_BORDER_WIDTH * 2);\n const height = PIXELS_PER_LEVEL;\n\n if (width < HIDE_THRESHOLD) {\n // We don't render nor it's children\n continue;\n }\n\n let offsetModifier = 0;\n let skipRender = false;\n const collapsedItemConfig = collapsedMap.get(item);\n const isCollapsedItem = collapsedItemConfig && collapsedItemConfig.collapsed;\n\n if (isCollapsedItem) {\n if (collapsedItemRendered === collapsedItemConfig.items[0]) {\n offsetModifier = direction === 'children' ? -1 : +1;\n skipRender = true;\n } else {\n // This is a case where we have another collapsed group right after different collapsed group, so we need to\n // reset.\n collapsedItemRendered = undefined;\n }\n } else {\n collapsedItemRendered = undefined;\n }\n\n if (!skipRender) {\n const barX = getBarX(item.start, totalViewTicks, rangeMin, pixelsPerTick);\n const barY = (item.level + levelOffset) * PIXELS_PER_LEVEL;\n\n let label = data.getLabel(item.itemIndexes[0]);\n if (isCollapsedItem) {\n collapsedItemRendered = item;\n }\n\n renderFunc(item, barX, barY, width, height, label, muted);\n }\n\n const nextList = direction === 'children' ? item.children : item.parents;\n if (nextList) {\n stack.unshift(...nextList.map((c) => ({ item: c, levelOffset: levelOffset + offsetModifier })));\n }\n }\n}\n\nfunction useColorFunction(\n totalTicks: number,\n totalTicksRight: number | undefined,\n colorScheme: ColorScheme | ColorSchemeDiff,\n theme: GrafanaTheme2,\n mutedColor: string,\n rangeMin: number,\n rangeMax: number,\n matchedLabels: Set<string> | undefined,\n topLevel: number\n) {\n return useCallback(\n function getColor(item: LevelItem, label: string, muted: boolean) {\n // If collapsed and no search we can quickly return the muted color\n if (muted && !matchedLabels) {\n // Collapsed are always grayed\n return mutedColor;\n }\n\n const barColor =\n item.valueRight !== undefined &&\n (colorScheme === ColorSchemeDiff.Default || colorScheme === ColorSchemeDiff.DiffColorBlind)\n ? getBarColorByDiff(item.value, item.valueRight!, totalTicks, totalTicksRight!, colorScheme)\n : colorScheme === ColorScheme.ValueBased\n ? getBarColorByValue(item.value, totalTicks, rangeMin, rangeMax)\n : getBarColorByPackage(label, theme);\n\n if (matchedLabels) {\n // Means we are searching, we use color for matches and gray the rest\n return matchedLabels.has(label) ? barColor.toHslString() : mutedColor;\n }\n\n // Mute if we are above the focused symbol\n return item.level > topLevel - 1 ? barColor.toHslString() : barColor.lighten(15).toHslString();\n },\n [totalTicks, totalTicksRight, colorScheme, theme, rangeMin, rangeMax, matchedLabels, topLevel, mutedColor]\n );\n}\n\nfunction useSetupCanvas(canvasRef: RefObject<HTMLCanvasElement>, wrapperWidth: number, numberOfLevels: number) {\n const [ctx, setCtx] = useState<CanvasRenderingContext2D>();\n\n useEffect(() => {\n if (!(numberOfLevels && canvasRef.current)) {\n return;\n }\n const ctx = canvasRef.current.getContext('2d')!;\n\n const height = PIXELS_PER_LEVEL * numberOfLevels;\n canvasRef.current.width = Math.round(wrapperWidth * window.devicePixelRatio);\n canvasRef.current.height = Math.round(height);\n canvasRef.current.style.width = `${wrapperWidth}px`;\n canvasRef.current.style.height = `${height / window.devicePixelRatio}px`;\n\n ctx.textBaseline = 'middle';\n ctx.font = 12 * window.devicePixelRatio + 'px monospace';\n ctx.strokeStyle = 'white';\n setCtx(ctx);\n }, [canvasRef, setCtx, wrapperWidth, numberOfLevels]);\n return ctx;\n}\n\n// Renders a text inside the node rectangle. It allows setting alignment of the text left or right which takes effect\n// when text is too long to fit in the rectangle.\nfunction renderLabel(\n ctx: CanvasRenderingContext2D,\n data: FlameGraphDataContainer,\n label: string,\n item: LevelItem,\n width: number,\n x: number,\n y: number,\n textAlign: TextAlign\n) {\n ctx.save();\n ctx.clip(); // so text does not overflow\n ctx.fillStyle = '#222';\n\n const displayValue = data.valueDisplayProcessor(item.value);\n const unit = displayValue.suffix ? displayValue.text + displayValue.suffix : displayValue.text;\n\n // We only measure name here instead of full label because of how we deal with the units and aligning later.\n const measure = ctx.measureText(label);\n const spaceForTextInRect = width - BAR_TEXT_PADDING_LEFT;\n\n let fullLabel = `${label} (${unit})`;\n let labelX = Math.max(x, 0) + BAR_TEXT_PADDING_LEFT;\n\n // We use the desired alignment only if there is not enough space for the text, otherwise we keep left alignment as\n // that will already show full text.\n if (measure.width > spaceForTextInRect) {\n ctx.textAlign = textAlign;\n // If aligned to the right we don't want to take the space with the unit label as the assumption is user wants to\n // mainly see the name. This also reflects how pyro/flamegraph works.\n if (textAlign === 'right') {\n fullLabel = label;\n labelX = x + width - BAR_TEXT_PADDING_LEFT;\n }\n }\n\n ctx.fillText(fullLabel, labelX, y + PIXELS_PER_LEVEL / 2 + 2);\n ctx.restore();\n}\n\n/**\n * Returns the X position of the bar. totalTicks * rangeMin is to adjust for any current zoom. So if we zoom to a\n * section of the graph we align and shift the X coordinates accordingly.\n * @param offset\n * @param totalTicks\n * @param rangeMin\n * @param pixelsPerTick\n */\nexport function getBarX(offset: number, totalTicks: number, rangeMin: number, pixelsPerTick: number) {\n return (offset - totalTicks * rangeMin) * pixelsPerTick;\n}\n"],"names":["ctx"],"mappings":"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"}
|
@@ -7,7 +7,7 @@ function mergeParentSubtrees(roots, data) {
|
|
7
7
|
function getParentSubtrees(roots) {
|
8
8
|
return roots.map((r) => {
|
9
9
|
var _a, _b;
|
10
|
-
if (!((_a = r.parents) == null ?
|
10
|
+
if (!((_a = r.parents) == null ? void 0 : _a.length)) {
|
11
11
|
return r;
|
12
12
|
}
|
13
13
|
const newRoot = {
|
@@ -28,7 +28,7 @@ function getParentSubtrees(roots) {
|
|
28
28
|
newNode.value = args.child.value;
|
29
29
|
args.child.parents = [newNode];
|
30
30
|
}
|
31
|
-
if ((_b = args.parent.parents) == null ?
|
31
|
+
if ((_b = args.parent.parents) == null ? void 0 : _b.length) {
|
32
32
|
stack.push({ child: newNode, parent: args.parent.parents[0] });
|
33
33
|
}
|
34
34
|
}
|
@@ -40,7 +40,7 @@ function mergeSubtrees(roots, data, direction = "children") {
|
|
40
40
|
const oppositeDirection = direction === "parents" ? "children" : "parents";
|
41
41
|
const levels = [];
|
42
42
|
const stack = [
|
43
|
-
{ previous:
|
43
|
+
{ previous: void 0, items: roots, level: 0 }
|
44
44
|
];
|
45
45
|
while (stack.length) {
|
46
46
|
const args = stack.shift();
|
@@ -59,7 +59,7 @@ function mergeSubtrees(roots, data, direction = "children") {
|
|
59
59
|
levels[args.level].push(newItem);
|
60
60
|
if (args.previous) {
|
61
61
|
newItem[oppositeDirection] = [args.previous];
|
62
|
-
const prevSiblingsVal = ((_a = args.previous[direction]) == null ?
|
62
|
+
const prevSiblingsVal = ((_a = args.previous[direction]) == null ? void 0 : _a.reduce((acc, node) => {
|
63
63
|
return acc + node.value;
|
64
64
|
}, 0)) || 0;
|
65
65
|
newItem.start = args.previous.start + prevSiblingsVal;
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"treeTransforms.js","sources":["../../../src/FlameGraph/treeTransforms.ts"],"sourcesContent":["import { groupBy } from 'lodash';\n\nimport { LevelItem } from './dataTransform';\n\ntype DataInterface = {\n getLabel: (index: number) => string;\n};\n\n// Merge parent subtree of the roots for the callers tree in the sandwich view of the flame graph.\nexport function mergeParentSubtrees(roots: LevelItem[], data: DataInterface): LevelItem[][] {\n const newRoots = getParentSubtrees(roots);\n return mergeSubtrees(newRoots, data, 'parents');\n}\n\n// Returns a subtrees per root that will have the parents resized to the same value as the root. When doing callers\n// tree we need to keep proper sizes of the parents, before we merge them, so we correctly attribute to the parents\n// only the value it contributed to the root.\n// So if we have something like:\n// [0/////////////]\n// [1//][4/////][6]\n// [2] [5/////]\n// [6] [6/][8/]\n// [7]\n// Taking all the node with '6' will create:\n// [0][0/]\n// [1][4/]\n// [2][5/][0]\n// [6][6/][6]\n// Which we can later merge.\nfunction getParentSubtrees(roots: LevelItem[]) {\n return roots.map((r) => {\n if (!r.parents?.length) {\n return r;\n }\n\n const newRoot = {\n ...r,\n children: [],\n };\n const stack: Array<{ child: undefined | LevelItem; parent: LevelItem }> = [\n { child: newRoot, parent: r.parents[0] },\n ];\n\n while (stack.length) {\n const args = stack.shift()!;\n const newNode = {\n ...args.parent,\n children: args.child ? [args.child] : [],\n parents: [],\n };\n\n if (args.child) {\n newNode.value = args.child.value;\n args.child.parents = [newNode];\n }\n\n if (args.parent.parents?.length) {\n stack.push({ child: newNode, parent: args.parent.parents[0] });\n }\n }\n return newRoot;\n });\n}\n\n// Merge subtrees into a single tree. Returns an array of levels for easy rendering. It assumes roots are mergeable,\n// meaning they represent the same unit of work (same label). Then we walk the tree in a specified direction,\n// merging nodes with the same label and same parent/child into single bigger node. This copies the tree (and all nodes)\n// as we are creating new merged nodes and modifying the parents/children.\nexport function mergeSubtrees(\n roots: LevelItem[],\n data: DataInterface,\n direction: 'parents' | 'children' = 'children'\n): LevelItem[][] {\n const oppositeDirection = direction === 'parents' ? 'children' : 'parents';\n const levels: LevelItem[][] = [];\n\n // Loop instead of recursion to be sure we don't blow stack size limit and save some memory. Each stack item is\n // basically a list of arrays you would pass to each level of recursion.\n const stack: Array<{ previous: undefined | LevelItem; items: LevelItem[]; level: number }> = [\n { previous: undefined, items: roots, level: 0 },\n ];\n\n while (stack.length) {\n const args = stack.shift()!;\n const indexes = args.items.flatMap((i) => i.itemIndexes);\n const newItem: LevelItem = {\n // We use the items value instead of value from the data frame, cause we could have changed it in the process\n value: args.items.reduce((acc, i) => acc + i.value, 0),\n itemIndexes: indexes,\n // these will change later\n children: [],\n parents: [],\n start: 0,\n level: args.level,\n };\n\n levels[args.level] = levels[args.level] || [];\n levels[args.level].push(newItem);\n\n if (args.previous) {\n // Not the first level, so we need to make sure we update previous items to keep the child/parent relationships\n // and compute correct new start offset for the item.\n newItem[oppositeDirection] = [args.previous];\n const prevSiblingsVal =\n args.previous[direction]?.reduce((acc, node) => {\n return acc + node.value;\n }, 0) || 0;\n newItem.start = args.previous.start + prevSiblingsVal;\n args.previous[direction]!.push(newItem);\n }\n\n const nextItems = args.items.flatMap((i) => i[direction] || []);\n // Group by label which for now is the only identifier by which we decide if node represents the same unit of work.\n const nextGroups = groupBy(nextItems, (c) => data.getLabel(c.itemIndexes[0]));\n for (const g of Object.values(nextGroups)) {\n stack.push({ previous: newItem, items: g, level: args.level + 1 });\n }\n }\n\n // Reverse the levels if we are doing callers tree, so we return levels in the correct order.\n if (direction === 'parents') {\n levels.reverse();\n levels.forEach((level, index) => {\n level.forEach((item) => {\n item.level = index;\n });\n });\n }\n\n return levels;\n}\n"],"names":[],"mappings":";;AASgB,SAAA,mBAAA,CAAoB,OAAoB,IAAoC,EAAA;AAC1F,EAAM,MAAA,QAAA,GAAW,kBAAkB,KAAK,CAAA;AACxC,EAAO,OAAA,aAAA,CAAc,QAAU,EAAA,IAAA,EAAM,SAAS,CAAA;AAChD;AAiBA,SAAS,kBAAkB,KAAoB,EAAA;AAC7C,EAAO,OAAA,KAAA,CAAM,GAAI,CAAA,CAAC,CAAM,KAAA;AA9B1B,IAAA,IAAA,EAAA,EAAA,EAAA;AA+BI,IAAA,IAAI,EAAC,CAAA,EAAA,GAAA,CAAA,CAAE,OAAF,KAAA,IAAA,GAAA,
|
1
|
+
{"version":3,"file":"treeTransforms.js","sources":["../../../src/FlameGraph/treeTransforms.ts"],"sourcesContent":["import { groupBy } from 'lodash';\n\nimport { LevelItem } from './dataTransform';\n\ntype DataInterface = {\n getLabel: (index: number) => string;\n};\n\n// Merge parent subtree of the roots for the callers tree in the sandwich view of the flame graph.\nexport function mergeParentSubtrees(roots: LevelItem[], data: DataInterface): LevelItem[][] {\n const newRoots = getParentSubtrees(roots);\n return mergeSubtrees(newRoots, data, 'parents');\n}\n\n// Returns a subtrees per root that will have the parents resized to the same value as the root. When doing callers\n// tree we need to keep proper sizes of the parents, before we merge them, so we correctly attribute to the parents\n// only the value it contributed to the root.\n// So if we have something like:\n// [0/////////////]\n// [1//][4/////][6]\n// [2] [5/////]\n// [6] [6/][8/]\n// [7]\n// Taking all the node with '6' will create:\n// [0][0/]\n// [1][4/]\n// [2][5/][0]\n// [6][6/][6]\n// Which we can later merge.\nfunction getParentSubtrees(roots: LevelItem[]) {\n return roots.map((r) => {\n if (!r.parents?.length) {\n return r;\n }\n\n const newRoot = {\n ...r,\n children: [],\n };\n const stack: Array<{ child: undefined | LevelItem; parent: LevelItem }> = [\n { child: newRoot, parent: r.parents[0] },\n ];\n\n while (stack.length) {\n const args = stack.shift()!;\n const newNode = {\n ...args.parent,\n children: args.child ? [args.child] : [],\n parents: [],\n };\n\n if (args.child) {\n newNode.value = args.child.value;\n args.child.parents = [newNode];\n }\n\n if (args.parent.parents?.length) {\n stack.push({ child: newNode, parent: args.parent.parents[0] });\n }\n }\n return newRoot;\n });\n}\n\n// Merge subtrees into a single tree. Returns an array of levels for easy rendering. It assumes roots are mergeable,\n// meaning they represent the same unit of work (same label). Then we walk the tree in a specified direction,\n// merging nodes with the same label and same parent/child into single bigger node. This copies the tree (and all nodes)\n// as we are creating new merged nodes and modifying the parents/children.\nexport function mergeSubtrees(\n roots: LevelItem[],\n data: DataInterface,\n direction: 'parents' | 'children' = 'children'\n): LevelItem[][] {\n const oppositeDirection = direction === 'parents' ? 'children' : 'parents';\n const levels: LevelItem[][] = [];\n\n // Loop instead of recursion to be sure we don't blow stack size limit and save some memory. Each stack item is\n // basically a list of arrays you would pass to each level of recursion.\n const stack: Array<{ previous: undefined | LevelItem; items: LevelItem[]; level: number }> = [\n { previous: undefined, items: roots, level: 0 },\n ];\n\n while (stack.length) {\n const args = stack.shift()!;\n const indexes = args.items.flatMap((i) => i.itemIndexes);\n const newItem: LevelItem = {\n // We use the items value instead of value from the data frame, cause we could have changed it in the process\n value: args.items.reduce((acc, i) => acc + i.value, 0),\n itemIndexes: indexes,\n // these will change later\n children: [],\n parents: [],\n start: 0,\n level: args.level,\n };\n\n levels[args.level] = levels[args.level] || [];\n levels[args.level].push(newItem);\n\n if (args.previous) {\n // Not the first level, so we need to make sure we update previous items to keep the child/parent relationships\n // and compute correct new start offset for the item.\n newItem[oppositeDirection] = [args.previous];\n const prevSiblingsVal =\n args.previous[direction]?.reduce((acc, node) => {\n return acc + node.value;\n }, 0) || 0;\n newItem.start = args.previous.start + prevSiblingsVal;\n args.previous[direction]!.push(newItem);\n }\n\n const nextItems = args.items.flatMap((i) => i[direction] || []);\n // Group by label which for now is the only identifier by which we decide if node represents the same unit of work.\n const nextGroups = groupBy(nextItems, (c) => data.getLabel(c.itemIndexes[0]));\n for (const g of Object.values(nextGroups)) {\n stack.push({ previous: newItem, items: g, level: args.level + 1 });\n }\n }\n\n // Reverse the levels if we are doing callers tree, so we return levels in the correct order.\n if (direction === 'parents') {\n levels.reverse();\n levels.forEach((level, index) => {\n level.forEach((item) => {\n item.level = index;\n });\n });\n }\n\n return levels;\n}\n"],"names":[],"mappings":"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"}
|
@@ -24,7 +24,6 @@ const FlameGraphContainer = ({
|
|
24
24
|
vertical,
|
25
25
|
showFlameGraphOnly,
|
26
26
|
disableCollapsing,
|
27
|
-
keepFocusOnDataChange,
|
28
27
|
getExtraContextMenuButtons
|
29
28
|
}) => {
|
30
29
|
const [focusedItemData, setFocusedItemData] = useState();
|
@@ -54,50 +53,23 @@ const FlameGraphContainer = ({
|
|
54
53
|
}
|
55
54
|
}, [selectedView, setSelectedView, containerWidth, vertical]);
|
56
55
|
const resetFocus = useCallback(() => {
|
57
|
-
setFocusedItemData(
|
56
|
+
setFocusedItemData(void 0);
|
58
57
|
setRangeMin(0);
|
59
58
|
setRangeMax(1);
|
60
59
|
}, [setFocusedItemData, setRangeMax, setRangeMin]);
|
61
|
-
|
62
|
-
setSandwichItem(
|
63
|
-
}
|
60
|
+
function resetSandwich() {
|
61
|
+
setSandwichItem(void 0);
|
62
|
+
}
|
64
63
|
useEffect(() => {
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
resetSandwich();
|
69
|
-
return;
|
70
|
-
}
|
71
|
-
if (dataContainer && focusedItemData) {
|
72
|
-
const item = (_a = dataContainer.getNodesWithLabel(focusedItemData.label)) == null ? undefined : _a[0];
|
73
|
-
if (item) {
|
74
|
-
setFocusedItemData({ ...focusedItemData, item });
|
75
|
-
const levels = dataContainer.getLevels();
|
76
|
-
const totalViewTicks = levels.length ? levels[0][0].value : 0;
|
77
|
-
setRangeMin(item.start / totalViewTicks);
|
78
|
-
setRangeMax((item.start + item.value) / totalViewTicks);
|
79
|
-
} else {
|
80
|
-
setFocusedItemData({
|
81
|
-
...focusedItemData,
|
82
|
-
item: {
|
83
|
-
start: 0,
|
84
|
-
value: 0,
|
85
|
-
itemIndexes: [],
|
86
|
-
children: [],
|
87
|
-
level: 0
|
88
|
-
}
|
89
|
-
});
|
90
|
-
setRangeMin(0);
|
91
|
-
setRangeMax(1);
|
92
|
-
}
|
93
|
-
}
|
94
|
-
}, [dataContainer, keepFocusOnDataChange]);
|
64
|
+
resetFocus();
|
65
|
+
resetSandwich();
|
66
|
+
}, [data, resetFocus]);
|
95
67
|
const onSymbolClick = useCallback(
|
96
68
|
(symbol) => {
|
97
69
|
if (search === symbol) {
|
98
70
|
setSearch("");
|
99
71
|
} else {
|
100
|
-
onTableSymbolClick == null ?
|
72
|
+
onTableSymbolClick == null ? void 0 : onTableSymbolClick(symbol);
|
101
73
|
setSearch(symbol);
|
102
74
|
resetFocus();
|
103
75
|
}
|
@@ -180,7 +152,7 @@ const FlameGraphContainer = ({
|
|
180
152
|
selectedView,
|
181
153
|
setSelectedView: (view) => {
|
182
154
|
setSelectedView(view);
|
183
|
-
onViewSelected == null ?
|
155
|
+
onViewSelected == null ? void 0 : onViewSelected(view);
|
184
156
|
},
|
185
157
|
containerWidth,
|
186
158
|
onReset: () => {
|
@@ -190,7 +162,7 @@ const FlameGraphContainer = ({
|
|
190
162
|
textAlign,
|
191
163
|
onTextAlignChange: (align) => {
|
192
164
|
setTextAlign(align);
|
193
|
-
onTextAlignSelected == null ?
|
165
|
+
onTextAlignSelected == null ? void 0 : onTextAlignSelected(align);
|
194
166
|
},
|
195
167
|
showResetButton: Boolean(focusedItemData || sandwichItem),
|
196
168
|
colorScheme,
|
@@ -208,7 +180,7 @@ const FlameGraphContainer = ({
|
|
208
180
|
);
|
209
181
|
};
|
210
182
|
function useColorScheme(dataContainer) {
|
211
|
-
const defaultColorScheme = (dataContainer == null ?
|
183
|
+
const defaultColorScheme = (dataContainer == null ? void 0 : dataContainer.isDiffFlamegraph()) ? ColorSchemeDiff.Default : ColorScheme.PackageBased;
|
212
184
|
const [colorScheme, setColorScheme] = useState(defaultColorScheme);
|
213
185
|
useEffect(() => {
|
214
186
|
setColorScheme(defaultColorScheme);
|
@@ -227,7 +199,7 @@ function useLabelSearch(search, data) {
|
|
227
199
|
}
|
228
200
|
return foundLabels;
|
229
201
|
}
|
230
|
-
return
|
202
|
+
return void 0;
|
231
203
|
}, [search, data]);
|
232
204
|
}
|
233
205
|
function getStyles(theme) {
|