Skip to content

Commit 3f9cffb

Browse files
authored
Merge pull request #31 from buggregator/issue/#28-xhprof-improves
Issue/#28 xhprof improves
2 parents 434bdec + 628e068 commit 3f9cffb

File tree

10 files changed

+386
-95
lines changed

10 files changed

+386
-95
lines changed

assets/digraph-builder.scss

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
.profiler-page-call-graph__graphviz svg {
2+
@apply bg-gray-700;
3+
4+
> text {
5+
@apply font-bold text-sm;
6+
}
7+
8+
.graph {
9+
> polygon {
10+
@apply fill-gray-700;
11+
}
12+
13+
> path {
14+
@apply fill-transparent;
15+
}
16+
}
17+
18+
.edge {
19+
> path {
20+
stroke-width: 2px;
21+
}
22+
23+
> text {
24+
@apply fill-white ml-2;
25+
26+
transform: translate(10px, 0px);
27+
}
28+
}
29+
30+
.node {
31+
@apply cursor-pointer;
32+
33+
> path {
34+
@apply rounded;
35+
36+
stroke-width: 1;
37+
}
38+
}
39+
40+
.pmu {
41+
> text {
42+
@apply fill-white;
43+
}
44+
45+
> path {
46+
@apply fill-red-600 stroke-red-800;
47+
}
48+
49+
&:hover > path {
50+
@apply fill-red-800;
51+
}
52+
}
53+
54+
.default {
55+
> text {
56+
@apply fill-gray-700;
57+
}
58+
59+
> path {
60+
@apply fill-gray-200 stroke-gray-400;
61+
}
62+
63+
&:hover > path {
64+
@apply fill-gray-300;
65+
}
66+
}
67+
68+
}

assets/index.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@import "vendor.css";
2+
@import "digraph-builder.scss";
23

34
body {
45
@apply bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-50 p-0;

components/ProfilePageFlamegraph/ProfilePageFlamegraph.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ export default defineComponent({
3737
},
3838
methods: {
3939
renderChart(canvas: HTMLCanvasElement, flameData: FlameChartNode) {
40+
if (!this.$refs.flamegraph) {
41+
return;
42+
}
4043
const { width = 1, height = 1 } = (
4144
this.$refs.flamegraph as HTMLElement
4245
).getBoundingClientRect();

components/ProfilerPage/ProfilerPage.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ export default defineComponent({
159159
}
160160
161161
.profiler-page__callstack {
162-
@apply w-full md:w-1/5 border-r border-gray-300 dark:border-gray-500;
162+
@apply w-full md:w-[250px] border-r border-gray-300 dark:border-gray-500;
163163
}
164164
165165
.profiler-page__stat {

components/ProfilerPageCallGraph/ProfilerPageCallGraph.vue

Lines changed: 99 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,16 @@
33
class="profiler-page-call-graph"
44
:class="{ 'profiler-page-call-graph--fullscreen': isFullscreen }"
55
>
6+
<div v-if="metricLoading" class="profiler-page-call-graph__loading-wr">
7+
<div class="profiler-page-call-graph__loading">
8+
<div></div>
9+
<div></div>
10+
<div></div>
11+
</div>
12+
</div>
13+
614
<div ref="graphviz" class="profiler-page-call-graph__graphviz"></div>
15+
716
<div class="profiler-page-call-graph__toolbar">
817
<button title="Full screen" @click="isFullscreen = !isFullscreen">
918
<IconSvg
@@ -14,25 +23,42 @@
1423
<button
1524
class="profiler-page-call-graph__toolbar-action"
1625
:class="{ 'font-bold': metric === 'cpu' }"
17-
@click="metric = 'cpu'"
26+
@click="setMetric('cpu')"
1827
>
1928
CPU
2029
</button>
2130
<button
2231
class="profiler-page-call-graph__toolbar-action"
2332
:class="{ 'font-bold': metric === 'pmu' }"
24-
@click="metric = 'pmu'"
33+
@click="setMetric('pmu')"
2534
>
2635
Memory change
2736
</button>
2837
<button
2938
class="profiler-page-call-graph__toolbar-action"
3039
:class="{ 'font-bold': metric === 'mu' }"
31-
@click="metric = 'mu'"
40+
@click="setMetric('mu')"
3241
>
3342
Memory usage
3443
</button>
3544
</div>
45+
46+
<div
47+
class="profiler-page-call-graph__toolbar profiler-page-call-graph__toolbar--right"
48+
>
49+
<label class="profiler-page-call-graph__toolbar-input-wr">
50+
Threshold:
51+
52+
<input
53+
class="profiler-page-call-graph__toolbar-input"
54+
type="number"
55+
:value="threshold"
56+
:min="0"
57+
:max="100"
58+
@input="setThreshold($event.target.value)"
59+
/>
60+
</label>
61+
</div>
3662
</div>
3763
</template>
3864

@@ -46,6 +72,7 @@ import IconSvg from "~/components/IconSvg/IconSvg.vue";
4672
import { defineComponent, PropType } from "vue";
4773
import { Profiler, ProfilerEdge } from "~/config/types";
4874
import { addSlashes, DigraphBuilder } from "~/utils/digraph-builder";
75+
import debounce from "lodash.debounce";
4976
5077
export default defineComponent({
5178
components: { IconSvg },
@@ -54,37 +81,55 @@ export default defineComponent({
5481
type: Object as PropType<Profiler>,
5582
required: true,
5683
},
57-
threshold: {
58-
type: Number,
59-
default: 1,
60-
},
6184
},
6285
emits: ["hover", "hide"],
6386
data() {
6487
return {
6588
isFullscreen: false,
6689
metric: "cpu",
90+
metricLoading: false,
91+
threshold: 1,
6792
};
6893
},
69-
watch: {
70-
threshold(): void {
71-
this.renderGraph();
72-
},
73-
metric(): void {
74-
this.renderGraph();
75-
},
76-
},
7794
created(): void {
78-
this.renderGraph();
95+
Graphviz.load().then(() => {
96+
this.graph = graphviz(this.$refs.graphviz, {})
97+
.width("100%")
98+
.height("100%")
99+
.fit(true);
100+
101+
this.renderGraph();
102+
});
79103
},
80104
beforeUnmount() {
81105
this.graph.destroy();
82106
},
83107
methods: {
84-
buildDigraph(): string {
85-
const builder = new DigraphBuilder(this.event.edges);
86-
87-
return builder.build(this.metric, this.threshold);
108+
setMetric(metric: string): void {
109+
this.metricLoading = true;
110+
111+
setTimeout(() => {
112+
this.metric = metric;
113+
this.renderGraph();
114+
this.metricLoading = false;
115+
}, 0);
116+
},
117+
setThreshold(threshold: number): void {
118+
this.metricLoading = true;
119+
120+
const prevThreshold = this.threshold;
121+
this.threshold = threshold;
122+
123+
return debounce(() => {
124+
if (!threshold || prevThreshold === threshold) {
125+
return;
126+
}
127+
128+
setTimeout(() => {
129+
this.renderGraph();
130+
this.metricLoading = false;
131+
}, 0);
132+
}, 1000)();
88133
},
89134
90135
findEdge(name: string): ProfilerEdge | null {
@@ -96,18 +141,19 @@ export default defineComponent({
96141
return null;
97142
}
98143
99-
return found[1] || null;
144+
return found[0] || null;
100145
},
101146
nodeHandler(): void {
102147
selectAll("g.node")
103148
.on("mouseover", (e, tag) => {
104149
const edge = this.findEdge(tag.key);
150+
105151
if (!edge) {
106152
return;
107153
}
108154
109155
this.$emit("hover", {
110-
name: edge.callee,
156+
callee: edge.callee,
111157
cost: edge.cost,
112158
position: {
113159
x: e.pageX,
@@ -120,19 +166,23 @@ export default defineComponent({
120166
});
121167
},
122168
renderGraph(): void {
123-
Graphviz.load().then(() => {
124-
this.graph = graphviz(this.$refs.graphviz, {})
125-
.width("100%")
126-
.height("100%")
127-
.fit(true)
128-
.renderDot(this.buildDigraph(), this.nodeHandler);
129-
});
169+
this.graph
170+
.renderDot(
171+
new DigraphBuilder(this.event.edges).build(
172+
this.metric,
173+
this.threshold
174+
),
175+
this.nodeHandler
176+
)
177+
.resetZoom();
130178
},
131179
},
132180
});
133181
</script>
134182

135183
<style lang="scss" scoped>
184+
@import "assets/mixins";
185+
136186
.profiler-page-call-graph {
137187
@apply relative flex rounded border border-gray-900 h-full;
138188
}
@@ -147,6 +197,10 @@ export default defineComponent({
147197
z-index: 9999;
148198
}
149199
200+
.profiler-page-call-graph__toolbar--right {
201+
@apply right-5 left-auto;
202+
}
203+
150204
.profiler-page-call-graph__toolbar-icon {
151205
@apply w-4 h-4 fill-blue-500;
152206
}
@@ -155,68 +209,27 @@ export default defineComponent({
155209
@apply text-xs uppercase text-gray-600;
156210
}
157211
158-
.profiler-page-call-graph__graphviz {
159-
@apply flex-1 justify-items-stretch items-stretch bg-white;
160-
161-
.graph {
162-
> polygon {
163-
@apply fill-gray-700;
164-
}
165-
166-
> path {
167-
@apply fill-transparent;
168-
}
169-
}
170-
171-
.edge {
172-
> path {
173-
stroke-width: 2px;
174-
}
175-
176-
> text {
177-
@apply fill-white ml-2;
178-
}
179-
}
180-
181-
.node {
182-
@apply cursor-pointer;
183-
184-
> path {
185-
@apply rounded;
186-
stroke-width: 1;
187-
}
188-
189-
&.pmu {
190-
> text {
191-
@apply fill-white;
192-
}
212+
.profiler-page-call-graph__toolbar-input-wr {
213+
@apply text-xs uppercase text-gray-600;
214+
}
193215
194-
> path {
195-
@apply fill-red-600 stroke-red-800;
196-
}
216+
.profiler-page-call-graph__toolbar-input {
217+
@apply border-b bg-transparent border-gray-600 text-gray-600 w-8;
218+
}
197219
198-
&:hover > path {
199-
@apply fill-red-800;
200-
}
201-
}
220+
.profiler-page-call-graph__loading-wr {
221+
@apply absolute m-auto top-0 left-0 right-0 bottom-0 flex justify-center items-center;
222+
}
202223
203-
&.default {
204-
> text {
205-
@apply fill-gray-700;
206-
}
224+
.profiler-page-call-graph__loading {
225+
@apply z-50;
207226
208-
> path {
209-
@apply fill-gray-200 stroke-gray-400;
210-
}
227+
@include loading;
228+
}
211229
212-
&:hover > path {
213-
@apply fill-gray-300;
214-
}
215-
}
230+
.profiler-page-call-graph__graphviz {
231+
@apply flex-1 justify-items-stretch items-stretch bg-white;
216232
217-
> text {
218-
@apply font-bold text-sm;
219-
}
220-
}
233+
max-height: 100vh;
221234
}
222235
</style>

0 commit comments

Comments
 (0)