diff options
author | Bingqian Liu <bql@google.com> | 2024-04-09 16:00:25 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2024-04-09 16:00:25 +0000 |
commit | 64b0f318a3d31eab5f5210e2d03513ad6e975200 (patch) | |
tree | 7cd4ac551da08b841fc3a92e41f28677df8b68f9 | |
parent | 5b3cddf26e007b7cf325872fcbf148b6097ebbe4 (diff) | |
parent | d4de9c1add33ba3daa9e2b65232089e36e696873 (diff) | |
download | perfetto-64b0f318a3d31eab5f5210e2d03513ad6e975200.tar.gz |
Merge "ui: add loading to flamegraph panel" into main
-rw-r--r-- | ui/src/assets/details.scss | 69 | ||||
-rw-r--r-- | ui/src/controller/flamegraph_controller.ts | 7 | ||||
-rw-r--r-- | ui/src/frontend/flamegraph_panel.ts | 98 | ||||
-rw-r--r-- | ui/src/frontend/globals.ts | 2 |
4 files changed, 97 insertions, 79 deletions
diff --git a/ui/src/assets/details.scss b/ui/src/assets/details.scss index f3e529a6e..d09690a2c 100644 --- a/ui/src/assets/details.scss +++ b/ui/src/assets/details.scss @@ -173,43 +173,6 @@ width: 50%; } } - &.flamegraph-profile { - display: flex; - justify-content: space-between; - align-content: center; - height: 30px; - padding: 0; - font-size: 12px; - * { - align-self: center; - } - .options { - display: inline-flex; - justify-content: space-around; - } - .details { - display: inline-flex; - justify-content: flex-end; - } - .title { - justify-self: start; - margin-left: 5px; - font-size: 14px; - margin-right: 10px; - } - .time { - justify-self: end; - margin-right: 10px; - } - .selected { - justify-self: end; - margin-right: 10px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - width: 200px; - } - } } table { @@ -709,3 +672,35 @@ .pf-noselection { height: 100%; } + +.flamegraph-profile { + height: 100%; + // This is required to position locally-scoped (i.e. non-full-screen) modal + // dialogs within the panel, as they use position: absolute. + position: relative; + + .time { + justify-self: end; + margin-right: 10px; + } + .selected { + justify-self: end; + margin-right: 10px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + width: 200px; + } + .flamegraph-content { + overflow: auto; + height: 100%; + + .loading-container { + font-size: larger; + display: flex; + align-items: center; + justify-content: center; + height: 100%; + } + } +} diff --git a/ui/src/controller/flamegraph_controller.ts b/ui/src/controller/flamegraph_controller.ts index a10252c99..b1c00a49a 100644 --- a/ui/src/controller/flamegraph_controller.ts +++ b/ui/src/controller/flamegraph_controller.ts @@ -300,6 +300,7 @@ export class FlamegraphController extends Controller<'main'> { await this.args.engine.query(`select value from stats where severity = 'error' and name = 'heap_graph_non_finalized_graph'`) ).firstRow({value: NUM}).value > 0; + flamegraphDetails.graphLoading = false; publishFlamegraphDetails(flamegraphDetails); } @@ -317,8 +318,10 @@ export class FlamegraphController extends Controller<'main'> { if (this.flamegraphDatasets.has(key)) { currentData = this.flamegraphDatasets.get(key)!; } else { - // TODO(b/330703412): Show loading state. - + publishFlamegraphDetails({ + ...globals.flamegraphDetails, + graphLoading: true, + }); // Collecting data for drawing flamegraph for selected profile. // Data needs to be in following format: // id, name, parent_id, depth, total_size diff --git a/ui/src/frontend/flamegraph_panel.ts b/ui/src/frontend/flamegraph_panel.ts index fea7fb2f4..0f4cba28a 100644 --- a/ui/src/frontend/flamegraph_panel.ts +++ b/ui/src/frontend/flamegraph_panel.ts @@ -30,6 +30,8 @@ import {Button} from '../widgets/button'; import {Icon} from '../widgets/icon'; import {Modal, ModalAttrs} from '../widgets/modal'; import {Popup} from '../widgets/popup'; +import {EmptyState} from '../widgets/empty_state'; +import {Spinner} from '../widgets/spinner'; import {Flamegraph, NodeRendering} from './flamegraph'; import {globals} from './globals'; @@ -37,7 +39,9 @@ import {debounce} from './rate_limiters'; import {Router} from './router'; import {getCurrentTrace} from './sidebar'; import {convertTraceToPprofAndDownload} from './trace_converter'; +import {ButtonBar} from '../widgets/button'; import {DurationWidget} from './widgets/duration'; +import {DetailsShell} from '../widgets/details_shell'; const HEADER_HEIGHT = 30; @@ -90,33 +94,31 @@ export class FlamegraphDetailsPanel implements m.ClassComponent { ? this.flamegraph.getHeight() + HEADER_HEIGHT : 0; return m( - '.details-panel', + '.flamegraph-profile', this.maybeShowModal(flamegraphDetails.graphIncomplete), m( - '.details-panel-heading.flamegraph-profile', - {onclick: (e: MouseEvent) => e.stopPropagation()}, - [ - m('div.options', [ - m( - 'div.title', - this.getTitle(), - this.profileType === ProfileType.MIXED_HEAP_PROFILE && + DetailsShell, + { + fillParent: true, + title: m( + 'div.title', + this.getTitle(), + this.profileType === ProfileType.MIXED_HEAP_PROFILE && + m( + Popup, + { + trigger: m(Icon, {icon: 'warning'}), + }, m( - Popup, - { - trigger: m(Icon, {icon: 'warning'}), - }, - m( - '', - {style: {width: '300px'}}, - 'This is a mixed java/native heap profile, free()s are not visualized. To visualize free()s, remove "all_heaps: true" from the config.', - ), + '', + {style: {width: '300px'}}, + 'This is a mixed java/native heap profile, free()s are not visualized. To visualize free()s, remove "all_heaps: true" from the config.', ), - ':', - ), - this.getViewingOptionButtons(), - ]), - m('div.details', [ + ), + ':', + ), + description: this.getViewingOptionButtons(), + buttons: [ m( 'div.selected', `Selected function: ${toSelectedCallsite( @@ -145,23 +147,39 @@ export class FlamegraphDetailsPanel implements m.ClassComponent { this.downloadPprof(); }, }), - ]), - ], - ), - m(`canvas[ref=canvas]`, { - style: `height:${height}px; width:100%`, - onmousemove: (e: MouseEvent) => { - const {offsetX, offsetY} = e; - this.onMouseMove({x: offsetX, y: offsetY}); - }, - onmouseout: () => { - this.onMouseOut(); + ], }, - onclick: (e: MouseEvent) => { - const {offsetX, offsetY} = e; - this.onMouseClick({x: offsetX, y: offsetY}); - }, - }), + m( + '.flamegraph-content', + flamegraphDetails.graphLoading + ? m( + '.loading-container', + m( + EmptyState, + { + icon: 'bar_chart', + title: 'Computing graph ...', + className: 'flamegraph-loading', + }, + m(Spinner, {easing: true}), + ), + ) + : m(`canvas[ref=canvas]`, { + style: `height:${height}px; width:100%`, + onmousemove: (e: MouseEvent) => { + const {offsetX, offsetY} = e; + this.onMouseMove({x: offsetX, y: offsetY}); + }, + onmouseout: () => { + this.onMouseOut(); + }, + onclick: (e: MouseEvent) => { + const {offsetX, offsetY} = e; + this.onMouseClick({x: offsetX, y: offsetY}); + }, + }), + ), + ), ); } else { return m( @@ -260,7 +278,7 @@ export class FlamegraphDetailsPanel implements m.ClassComponent { getViewingOptionButtons(): m.Children { return m( - 'div', + ButtonBar, ...FlamegraphDetailsPanel.selectViewingOptions( assertExists(this.profileType), ), diff --git a/ui/src/frontend/globals.ts b/ui/src/frontend/globals.ts index 1af13996a..5cecd7e26 100644 --- a/ui/src/frontend/globals.ts +++ b/ui/src/frontend/globals.ts @@ -160,6 +160,8 @@ export interface FlamegraphDetails { // When heap_graph_non_finalized_graph has a count >0, we mark the graph // as incomplete. graphIncomplete?: boolean; + // About to show a new graph whose data is not ready yet. + graphLoading?: boolean; } export interface CpuProfileDetails { |