Skip to content

Commit 4f41bb7

Browse files
committed
feat: add module flamegraph
1 parent 0f368b1 commit 4f41bb7

File tree

20 files changed

+533
-355
lines changed

20 files changed

+533
-355
lines changed
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
<script setup lang="ts">
2+
import type { TreeNodeInput } from 'nanovis'
3+
import type { ModuleInfo, SessionContext } from '~~/shared/types'
4+
import { Flamegraph, normalizeTreeNode } from 'nanovis'
5+
import { computed, defineProps, onMounted, ref, useTemplateRef } from 'vue'
6+
7+
const props = defineProps<{
8+
info: ModuleInfo
9+
session: SessionContext
10+
}>()
11+
12+
const n = (node: TreeNodeInput<any>) => normalizeTreeNode(node, undefined, false)
13+
14+
const tree = computed(() => {
15+
const resolveIds = props.info.resolve_ids.map((id, idx) => n({
16+
id: `resolveId-${idx}`,
17+
text: id.plugin_name,
18+
size: id.duration,
19+
}))
20+
const loads = props.info.loads.map((load, idx) => n({
21+
id: `load-${idx}`,
22+
text: load.plugin_name,
23+
size: load.duration,
24+
}))
25+
const transforms = props.info.transforms.map((transform, idx) => n({
26+
id: `transform-${idx}`,
27+
text: transform.plugin_name,
28+
size: transform.duration,
29+
}))
30+
const children = [
31+
n({
32+
id: '~resolves',
33+
text: 'resolve',
34+
children: resolveIds,
35+
}),
36+
n({
37+
id: '~loads',
38+
text: 'load',
39+
children: loads,
40+
}),
41+
n({
42+
id: '~transforms',
43+
text: 'transform',
44+
children: transforms,
45+
}),
46+
]
47+
48+
return n({
49+
id: '~root',
50+
text: 'Module Flamegraph',
51+
children,
52+
})
53+
})
54+
55+
const hoverNode = ref<{
56+
plugin_name: string
57+
duration: number
58+
} | null>(null)
59+
const hoverX = ref<number>(0)
60+
const hoverY = ref<number>(0)
61+
const el = useTemplateRef<HTMLDivElement>('el')
62+
63+
onMounted(() => {
64+
const flamegraph = new Flamegraph(tree.value, {
65+
animate: true,
66+
palette: {
67+
fg: '#888',
68+
},
69+
getSubtext: (node) => {
70+
const p = node.size / tree.value.size * 100
71+
if (p > 15 && p !== 100) {
72+
return `${p.toFixed(1)}%`
73+
}
74+
return undefined
75+
},
76+
onHover(node, e) {
77+
if (!node) {
78+
hoverNode.value = null
79+
return
80+
}
81+
if (e) {
82+
hoverX.value = e.clientX
83+
hoverY.value = e.clientY
84+
}
85+
hoverNode.value = {
86+
plugin_name: node.text!,
87+
duration: node.size,
88+
}
89+
},
90+
})
91+
92+
el.value!.appendChild(flamegraph.el)
93+
94+
return () => {
95+
flamegraph.dispose()
96+
}
97+
})
98+
</script>
99+
100+
<template>
101+
<div relative border="t base" pb10 py1>
102+
<Teleport to="body">
103+
<div
104+
v-if="hoverNode"
105+
border="~ base" rounded shadow px2 py1 fixed
106+
z-panel-content bg-glass pointer-events-none text-sm
107+
:style="{ left: `${hoverX}px`, top: `${hoverY}px` }"
108+
>
109+
<div font-bold font-mono>
110+
{{ hoverNode.plugin_name }}
111+
</div>
112+
<DisplayDuration :duration="hoverNode.duration" />
113+
</div>
114+
</Teleport>
115+
<div ref="el" min-h-30 />
116+
</div>
117+
</template>

packages/devtools/src/app/components/data/RawEventsTable.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script setup lang="ts">
2-
import type { RolldownEvent } from '~/../node/rolldown/events-manager'
3-
import type { SessionContext } from '~/types/data'
2+
import type { RolldownEvent } from '~~/node/rolldown/events-manager'
3+
import type { SessionContext } from '~~/shared/types'
44
import { Dropdown as VDropdown } from 'floating-vue'
55
import { defineProps, withDefaults } from 'vue'
66

packages/devtools/src/app/components/display/ModuleId.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script setup lang="ts">
22
import type { ModuleImport } from '@rolldown/debug'
3-
import type { SessionContext } from '../../types/data'
3+
import type { SessionContext } from '~~/shared/types'
44
import { useRoute } from '#app/composables/router'
55
import { NuxtLink } from '#components'
66
import { Tooltip } from 'floating-vue'

packages/devtools/src/app/components/flowmap/ModuleFlow.vue

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<script setup lang="ts">
2-
import type { ModuleInfo, RolldownModuleFlowNode, RolldownModuleLoadNoChanges, RolldownModuleTransformInfo, RolldownModuleTransformNoChanges } from '../../../node/rpc/functions/rolldown-get-module-info'
3-
import type { SessionContext } from '../../types/data'
2+
import type { ModuleInfo, RolldownModuleFlowNode, RolldownModuleLoadNoChanges, RolldownModuleTransformInfo, RolldownModuleTransformNoChanges, SessionContext } from '~~/shared/types'
43
import { Menu as VMenu } from 'floating-vue'
54
import { computed, ref, shallowRef, toRefs } from 'vue'
65
import PluginName from '../display/PluginName.vue'

packages/devtools/src/app/components/flowmap/ModuleFlowLoader.vue

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script setup lang="ts">
2-
import type { SessionContext } from '~/types/data'
2+
import type { SessionContext } from '~~/shared/types'
33
import { computedAsync } from '@vueuse/core'
44
import { backend } from '~/state/backend'
55
@@ -19,12 +19,17 @@ const info = computedAsync(async () => {
1919

2020
<template>
2121
<div>
22-
<FlowmapModuleFlow
23-
v-if="info"
24-
p4
25-
:info
26-
:session
27-
/>
22+
<template v-if="info">
23+
<FlowmapModuleFlow
24+
p4
25+
:info
26+
:session
27+
/>
28+
<ChartModuleFlamegraph
29+
:info
30+
:session="session"
31+
/>
32+
</template>
2833
<div v-else>
2934
<!-- TODO: Better loading screen -->
3035
<div>Loading...</div>

packages/devtools/src/app/components/flowmap/NodeModuleInfo.vue

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<script setup lang="ts">
2-
import type { RolldownModuleFlowNode } from '../../../node/rpc/functions/rolldown-get-module-info'
3-
import type { SessionContext } from '~/types/data'
2+
import type { RolldownModuleFlowNode, SessionContext } from '~~/shared/types'
43
import { computed } from 'vue'
54
import { isFlowmapSwapping } from '~/state/flowmap'
65

packages/devtools/src/app/components/modules/FlatList.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script setup lang="ts">
2-
import type { ModuleListItem, SessionContext } from '../../types/data'
2+
import type { ModuleListItem, SessionContext } from '~~/shared/types'
33
44
defineProps<{
55
session: SessionContext

packages/devtools/src/app/components/modules/Graph.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script setup lang="ts">
22
import type { HierarchyLink, HierarchyNode } from 'd3-hierarchy'
3-
import type { ModuleImport, ModuleListItem, SessionContext } from '../../types/data'
3+
import type { ModuleImport, ModuleListItem, SessionContext } from '~~/shared/types'
44
import { useEventListener } from '@vueuse/core'
55
import { hierarchy, tree } from 'd3-hierarchy'
66
import { linkHorizontal, linkVertical } from 'd3-shape'

packages/devtools/src/app/pages/session/[session].vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<script setup lang="ts">
2-
import type { ModuleListItem, SessionContext } from '../../types/data'
2+
import type { ModuleListItem, SessionContext } from '~~/shared/types'
33
import { useRoute } from '#app/composables/router'
44
import { computed, onMounted, reactive, ref, shallowRef } from 'vue'
5+
import { backend } from '~/state/backend'
56
import { getFileTypeFromName } from '~/utils/icon'
6-
import { backend } from '../../state/backend'
77
88
const params = useRoute().params as {
99
session: string

packages/devtools/src/app/pages/session/[session]/graph/index.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script setup lang="ts">
2-
import type { SessionContext } from '~/types/data'
2+
import type { SessionContext } from '~~/shared/types'
33
import { useRoute, useRouter } from '#app/composables/router'
44
import { clearUndefined } from '@antfu/utils'
55
import { computedWithControl, debouncedWatch } from '@vueuse/core'
@@ -187,7 +187,7 @@ const display = ref<'list' | 'graph'>('list')
187187
<div
188188
v-if="route.query.module"
189189
:key="(route.query.module as string)"
190-
fixed right-0 bottom-20 top-20 z-panel-content min-w-200 bg-glass border="l t b base rounded-l-xl"
190+
fixed right-0 bottom-0 top-20 z-panel-content min-w-200 of-auto bg-glass border="l t base rounded-tl-xl"
191191
>
192192
<FlowmapModuleFlowLoader
193193
:module="(route.query.module as string)"

0 commit comments

Comments
 (0)