Skip to content

Commit f327c5a

Browse files
Port cpu profile flame graph to draw to canvas. (#506)
* Port cpu profile flame graph to draw to canvas.
1 parent e27d49e commit f327c5a

File tree

9 files changed

+731
-206
lines changed

9 files changed

+731
-206
lines changed

packages/devtools/lib/src/timeline/cpu_flame_chart.dart

Lines changed: 0 additions & 170 deletions
This file was deleted.

packages/devtools/lib/src/timeline/cpu_profile_protocol.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ class CpuProfileData {
1111
CpuProfileData(this.cpuProfileResponse)
1212
: sampleCount = cpuProfileResponse.json['sampleCount'],
1313
samplePeriod = cpuProfileResponse.json['samplePeriod'],
14+
timeExtentMicros = cpuProfileResponse.json['timeExtentMicros'],
1415
stackFramesJson = cpuProfileResponse.json['stackFrames'],
1516
stackTraceEvents = cpuProfileResponse.json['traceEvents'] {
1617
_processStackFrames(cpuProfileResponse);
@@ -19,6 +20,7 @@ class CpuProfileData {
1920
final Response cpuProfileResponse;
2021
final int sampleCount;
2122
final int samplePeriod;
23+
final int timeExtentMicros;
2224
final Map<String, dynamic> stackFramesJson;
2325

2426
/// Trace events associated with the last stackFrame in each sample (i.e. the

packages/devtools/lib/src/timeline/event_details.dart

Lines changed: 59 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,11 @@ import '../ui/primer.dart';
1212
import '../utils.dart';
1313
import 'cpu_bottom_up.dart';
1414
import 'cpu_call_tree.dart';
15-
import 'cpu_flame_chart.dart';
1615
import 'cpu_profile_protocol.dart';
16+
import 'flame_chart_canvas.dart';
1717
import 'frame_flame_chart.dart';
1818
import 'timeline_protocol.dart';
1919

20-
// TODO(kenzie): this should be removed once the cpu flame chart is optimized.
21-
const bool showCpuFlameChart = false;
22-
2320
class EventDetails extends CoreElement {
2421
EventDetails() : super('div') {
2522
flex();
@@ -146,10 +143,9 @@ class _UiEventDetails extends CoreElement {
146143
layoutVertical();
147144
flex();
148145

149-
if (showCpuFlameChart) {
150-
flameChart = CpuFlameChart();
151-
} else {
152-
flameChart = div(c: 'ui-details-section');
146+
flameChart = div(c: 'ui-details-section');
147+
148+
if (!showCpuFlameChart) {
153149
flameChart.add(div(text: 'CPU flame chart coming soon', c: 'message'));
154150
}
155151

@@ -163,8 +159,6 @@ class _UiEventDetails extends CoreElement {
163159
if (showCpuFlameChart) {
164160
add(stackFrameDetails);
165161
}
166-
167-
onSelectedCpuFlameChartItem.listen(updateStackFrameDetails);
168162
}
169163

170164
static const String stackFrameDetailsDefaultText = '[No function selected]';
@@ -174,15 +168,20 @@ class _UiEventDetails extends CoreElement {
174168
CpuCallTree callTree;
175169
CoreElement stackFrameDetails;
176170

171+
EventDetailsTabType selectedTab = EventDetailsTabType.flameChart;
172+
173+
bool showingFlameChartError = false;
174+
177175
TimelineEvent event;
178176

179177
CpuProfileData cpuProfileData;
180178

181179
void showTab(EventDetailsTabType tabType) {
180+
selectedTab = tabType;
182181
switch (tabType) {
183182
case EventDetailsTabType.flameChart:
184183
flameChart.attribute('hidden', false);
185-
stackFrameDetails.attribute('hidden', false);
184+
stackFrameDetails.attribute('hidden', showingFlameChartError);
186185
bottomUp.attribute('hidden', true);
187186
callTree.attribute('hidden', true);
188187
break;
@@ -201,6 +200,45 @@ class _UiEventDetails extends CoreElement {
201200
}
202201
}
203202

203+
Future<void> _drawFlameChart() async {
204+
final Response response =
205+
await serviceManager.service.getCpuProfileTimeline(
206+
serviceManager.isolateManager.selectedIsolate.id,
207+
event.startTime,
208+
event.duration,
209+
);
210+
211+
cpuProfileData = CpuProfileData(response);
212+
213+
if (cpuProfileData.stackFrames.isEmpty) {
214+
_updateFlameChartForError(div(
215+
text: 'CPU profile unavailable for time range'
216+
' [${event.startTime} - ${event.endTime}]',
217+
c: 'message',
218+
));
219+
return;
220+
}
221+
222+
final flameChartCanvas = FlameChartCanvas(
223+
data: cpuProfileData,
224+
flameChartWidth: flameChart.element.clientWidth,
225+
flameChartHeight:
226+
cpuProfileData.cpuProfileRoot.depth * rowHeightWithPadding,
227+
);
228+
229+
flameChartCanvas.onStackFrameSelected.listen((CpuStackFrame stackFrame) {
230+
_updateStackFrameDetails(stackFrame);
231+
});
232+
233+
flameChart.add(flameChartCanvas.element);
234+
}
235+
236+
void _updateFlameChartForError(CoreElement errorDiv) {
237+
flameChart.add(errorDiv);
238+
showingFlameChartError = true;
239+
stackFrameDetails.attribute('hidden', true);
240+
}
241+
204242
Future<void> update(TimelineEvent event) async {
205243
if (event == this.event) {
206244
return;
@@ -212,17 +250,11 @@ class _UiEventDetails extends CoreElement {
212250
final Spinner spinner = Spinner()..clazz('cpu-profile-spinner');
213251
add(spinner);
214252

215-
final Response response =
216-
await serviceManager.service.getCpuProfileTimeline(
217-
serviceManager.isolateManager.selectedIsolate.id,
218-
event.startTime,
219-
event.duration,
220-
);
221-
222-
cpuProfileData = CpuProfileData(response);
223-
224-
if (showCpuFlameChart) {
225-
(flameChart as CpuFlameChart).update(cpuProfileData);
253+
try {
254+
await _drawFlameChart();
255+
} catch (e) {
256+
_updateFlameChartForError(div(
257+
text: 'Error retrieving CPU profile: ${e.toString()}', c: 'message'));
226258
}
227259

228260
spinner.element.remove();
@@ -233,11 +265,14 @@ class _UiEventDetails extends CoreElement {
233265
void reset() {
234266
flameChart.clear();
235267
stackFrameDetails.clear();
268+
showingFlameChartError = false;
269+
stackFrameDetails.attribute(
270+
'hidden', selectedTab != EventDetailsTabType.flameChart);
236271
cpuProfileData = null;
237272
}
238273

239-
void updateStackFrameDetails(CpuFlameChartItem item) {
240-
stackFrameDetails.text = item.stackFrame.toString();
274+
void _updateStackFrameDetails(CpuStackFrame stackFrame) {
275+
stackFrameDetails.text = stackFrame.toString();
241276
}
242277
}
243278

packages/devtools/lib/src/timeline/flame_chart.dart

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import '../ui/flutter_html_shim.dart';
1414
import '../ui/theme.dart';
1515
import 'timeline.dart';
1616

17+
// TODO(kenzie): delete this file once frame_flame_chart is ported to canvas.
18+
1719
const selectedFlameChartItemColor =
1820
ThemedColor(mainUiColorSelectedLight, mainUiColorSelectedDark);
1921

@@ -202,6 +204,11 @@ class FlameChartItem {
202204
/// text does not get too close to the right hand size of each div.
203205
static const labelPaddingRight = 4;
204206

207+
static const selectedBorderColor = ThemedColor(
208+
Color(0x5A1B1F23),
209+
Color(0x5A1B1F23),
210+
);
211+
205212
/// Left value for the flame chart item at zoom level 1.
206213
final num startingLeft;
207214

@@ -256,9 +263,7 @@ class FlameChartItem {
256263
..backgroundColor =
257264
colorToCss(selected ? selectedFlameChartItemColor : backgroundColor)
258265
..border = selected ? '1px solid' : 'none'
259-
..borderColor = selected
260-
? colorToCss(const Color(0x5A1B1F23))
261-
: colorToCss(const Color(0x231B1F23));
266+
..borderColor = colorToCss(selectedBorderColor);
262267
itemLabel.style.color =
263268
colorToCss(selected ? selectedTextColor : defaultTextColor);
264269
}

0 commit comments

Comments
 (0)