Skip to content

Commit cad3ec8

Browse files
authored
[DebuggerV2] Flesh out graph execution data display (#3528)
* Motivation for features / changes * Continue developing DebuggerV2 plugin, specifically its GraphExecutionContainer that visualizes the details of the intra-graph execution events. * Technical description of changes * Add necessary actions, selectors, reducers and effects to support the lazy, paged loading of `GraphExecution`s * Add a `cdk-virtual-scroll-viewport` to `GraphExecutionComponent` * The lazy loading is triggered by scrolling events on the `cdk-virtual-scroll-viewport` * The displaying detailed debug-tensor summaries such as dtype, rank, shape, and numeric breakdown will be added in the follow PRs. This PR just adds displaying of the tensor name and op type in the `cdk-virtual-scroll-viewport`. * Screenshots of UI changes * Loaded state: ![image](https://user-images.githubusercontent.com/16824702/79614243-24f6e500-80ce-11ea-8b9f-412cb831a449.png) * Loading state (mat-spinner to be added in follow-up CLs): ![image](https://user-images.githubusercontent.com/16824702/79614131-e19c7680-80cd-11ea-8dad-2dfd4b998ada.png) * Detailed steps to verify changes work correctly (as executed by you) * Unit tests added * Manual testing against logdirs with real tfdbg2 data of different sizes
1 parent 6ab1d00 commit cad3ec8

21 files changed

+917
-26
lines changed

tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/actions/debugger_actions.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
import {
2828
ExecutionDigestsResponse,
2929
ExecutionDataResponse,
30+
GraphExecutionDataResponse,
3031
SourceFileResponse,
3132
} from '../data_source/tfdbg2_data_source';
3233

@@ -144,6 +145,24 @@ export const numGraphExecutionsLoaded = createAction(
144145
props<{numGraphExecutions: number}>()
145146
);
146147

148+
export const graphExecutionDataRequested = createAction(
149+
'[Debugger] Intra-Graph Execution Data Requested',
150+
props<{pageIndex: number}>()
151+
);
152+
153+
export const graphExecutionDataLoaded = createAction(
154+
'[Debugger] Intra-Graph Execution Data Loaded',
155+
props<GraphExecutionDataResponse>()
156+
);
157+
158+
export const graphExecutionScrollToIndex = createAction(
159+
'[Debugger] Scroll Intra-Graph Execution List to Given Index',
160+
props<{index: number}>()
161+
);
162+
163+
/**
164+
* Actions related to source files and stack traces.
165+
*/
147166
export const sourceFileListRequested = createAction(
148167
'[Debugger] Source File List Requested.'
149168
);

tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/debugger_component.css

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,18 @@ limitations under the License.
1515

1616
.bottom-section {
1717
border-top: 1px solid rgba(0, 0, 0, 0.12);
18-
height: 353px;
18+
height: 34%;
1919
padding-top: 6px;
2020
width: 100%;
2121
}
2222

23+
.debugger-container {
24+
background-color: #fff;
25+
height: 100%;
26+
}
27+
2328
.top-section {
24-
height: 360px;
29+
height: 66%;
2530
padding: 6px 0;
2631
width: 100%;
2732
}

tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/debugger_component.ng.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
limitations under the License.
1616
-->
1717

18-
<div>
18+
<div class="debugger-container">
1919
<tf-debugger-v2-inactive
2020
*ngIf="runIds.length === 0; else dataAvailable"
2121
></tf-debugger-v2-inactive>

tensorboard/plugins/debugger_v2/tf_debugger_v2_plugin/effects/debugger_effects.ts

Lines changed: 120 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {Store} from '@ngrx/store';
1717
import {Actions, createEffect, ofType} from '@ngrx/effects';
1818
import {merge, Observable} from 'rxjs';
1919
import {
20+
debounceTime,
2021
filter,
2122
map,
2223
mergeMap,
@@ -37,6 +38,9 @@ import {
3738
executionScrollLeft,
3839
executionScrollRight,
3940
executionScrollToIndex,
41+
graphExecutionDataLoaded,
42+
graphExecutionDataRequested,
43+
graphExecutionScrollToIndex,
4044
numAlertsAndBreakdownLoaded,
4145
numAlertsAndBreakdownRequested,
4246
numExecutionsLoaded,
@@ -61,6 +65,11 @@ import {
6165
getExecutionPageSize,
6266
getExecutionScrollBeginIndex,
6367
getFocusedSourceFileContent,
68+
getGraphExecutionDataPageLoadedSizes,
69+
getGraphExecutionDisplayCount,
70+
getGraphExecutionPageSize,
71+
getGraphExecutionScrollBeginIndex,
72+
getGraphExecutionDataLoadingPages,
6473
getNumExecutions,
6574
getNumExecutionsLoaded,
6675
getLoadedAlertsOfFocusedType,
@@ -541,6 +550,109 @@ export class DebuggerEffects {
541550
);
542551
}
543552

553+
/**
554+
* Emits when scrolling event leads to need to load new intra-graph execution
555+
* data.
556+
*
557+
* The returned observable contains the
558+
* - runId: active runId,
559+
* - missingPage: indices of missing `GraphExecution` pages that need to be
560+
* loaded by a downstream pipe.
561+
* - pageSize: GraphExecution data page size.
562+
* - numGraphExecutions: Current total number of `GraphExecution`s.
563+
*/
564+
private onGraphExecutionScroll(): Observable<{
565+
runId: string;
566+
missingPages: number[];
567+
pageSize: number;
568+
numGraphExecutions: number;
569+
}> {
570+
return this.actions$.pipe(
571+
ofType(graphExecutionScrollToIndex),
572+
debounceTime(100),
573+
withLatestFrom(
574+
this.store.select(getActiveRunId),
575+
this.store.select(getNumGraphExecutions),
576+
this.store.select(getGraphExecutionScrollBeginIndex)
577+
),
578+
filter(([, runId, numGraphExecutions]) => {
579+
return runId !== null && numGraphExecutions > 0;
580+
}),
581+
map(([, runId, numGraphExecutions, scrollBeginIndex]) => ({
582+
runId,
583+
numGraphExecutions,
584+
scrollBeginIndex,
585+
})),
586+
withLatestFrom(
587+
this.store.select(getGraphExecutionPageSize),
588+
this.store.select(getGraphExecutionDisplayCount),
589+
this.store.select(getGraphExecutionDataLoadingPages),
590+
this.store.select(getGraphExecutionDataPageLoadedSizes)
591+
),
592+
map(
593+
([
594+
{runId, numGraphExecutions, scrollBeginIndex},
595+
pageSize,
596+
displayCount,
597+
loadingPages,
598+
pageLoadedSizes,
599+
]) => {
600+
let missingPages: number[] = getMissingPages(
601+
scrollBeginIndex,
602+
Math.min(scrollBeginIndex + displayCount, numGraphExecutions),
603+
pageSize,
604+
numGraphExecutions,
605+
pageLoadedSizes
606+
);
607+
// Omit pages that are already loading.
608+
missingPages = missingPages.filter(
609+
(page) => loadingPages.indexOf(page) === -1
610+
);
611+
return {
612+
runId: runId!,
613+
missingPages,
614+
pageSize,
615+
numGraphExecutions,
616+
};
617+
}
618+
)
619+
);
620+
}
621+
622+
private loadGraphExecutionPages(
623+
prevStream$: Observable<{
624+
runId: string;
625+
missingPages: number[];
626+
pageSize: number;
627+
numGraphExecutions: number;
628+
}>
629+
): Observable<void> {
630+
return prevStream$.pipe(
631+
filter(({missingPages}) => missingPages.length > 0),
632+
tap(({missingPages}) => {
633+
missingPages.forEach((pageIndex) => {
634+
this.store.dispatch(graphExecutionDataRequested({pageIndex}));
635+
});
636+
}),
637+
mergeMap(({runId, missingPages, pageSize, numGraphExecutions}) => {
638+
const begin = missingPages[0] * pageSize;
639+
const end = Math.min(
640+
(missingPages[missingPages.length - 1] + 1) * pageSize,
641+
numGraphExecutions
642+
);
643+
return this.dataSource.fetchGraphExecutionData(runId!, begin, end).pipe(
644+
tap((graphExecutionDataResponse) => {
645+
this.store.dispatch(
646+
graphExecutionDataLoaded(graphExecutionDataResponse)
647+
);
648+
}),
649+
map(() => void null)
650+
);
651+
// TODO(cais): Add catchError() to pipe.
652+
})
653+
);
654+
}
655+
544656
/**
545657
* Emits when user focuses on an alert type.
546658
*
@@ -766,6 +878,8 @@ export class DebuggerEffects {
766878
*
767879
* on source file requested ---> fetch source file
768880
*
881+
* on graph-execution scroll --> fetch graph-execution data
882+
*
769883
**/
770884
this.loadData$ = createEffect(
771885
() => {
@@ -827,14 +941,19 @@ export class DebuggerEffects {
827941

828942
const onSourceFileFocused$ = this.onSourceFileFocused();
829943

944+
const onGraphExecutionScroll$ = this.loadGraphExecutionPages(
945+
this.onGraphExecutionScroll()
946+
);
947+
830948
// ExecutionDigest and ExecutionData can be loaded in parallel.
831949
return merge(
832950
onNumAlertsLoaded$,
833951
onExcutionDigestLoaded$,
834952
onExecutionDataLoaded$,
835953
onNumGraphExecutionLoaded$,
836954
loadSourceFileList$,
837-
onSourceFileFocused$
955+
onSourceFileFocused$,
956+
onGraphExecutionScroll$
838957
).pipe(
839958
// createEffect expects an Observable that emits {}.
840959
map(() => ({}))

0 commit comments

Comments
 (0)