diff --git a/locales/en/public.json b/locales/en/public.json index 44c2e82d7..9951ad069 100644 --- a/locales/en/public.json +++ b/locales/en/public.json @@ -365,7 +365,8 @@ "DATETIME": "Date and Time" }, "Diagnostics": { - "TARGET_THREAD_DUMPS_TAB_TITLE": "Targets" + "TARGET_THREAD_DUMPS_TAB_TITLE": "Targets", + "TARGET_HEAP_DUMPS_TAB_TITLE": "Targets" }, "DiagnosticsCard": { "DIAGNOSTICS_ACTION_FAILURE": "Diagnostics Failure: {{kind}}", @@ -373,12 +374,24 @@ "DIAGNOSTICS_CARD_DESCRIPTION_FULL": "Perform diagonstic operations from a list of supported operations on the target.", "DIAGNOSTICS_CARD_TITLE": "Diagnostics", "DIAGNOSTICS_GC_BUTTON": "Invoke Garbage Collection", + "DIAGNOSTICS_HEAP_DUMP_BUTTON": "Invoke Heap Dump", + "DIAGNOSTICS_HEAP_REDIRECT_BUTTON": "View collected Heap Dumps", "DIAGNOSTICS_THREAD_DUMP_BUTTON": "Invoke Thread Dump", "DIAGNOSTICS_THREAD_DUMP_TABLE_TOOLTIP": "View captured Thread Dumps", "DIAGONSTICS_THREAD_REDIRECT_BUTTON": "View collected Thread Dumps", "KINDS": { "GC": "Garbage Collection", - "THREAD_DUMP": "Thread Dump" + "HEAP_DUMP": "Heap Dump", + "THREADS": "Thread Dump" + } + }, + "HeapDumps": { + "SEARCH_PLACEHOLDER": "Search Heap Dumps", + "DELETION_FAILURE_CATEGORY": "Heap Dump Deletion Failure", + "DELETION_FAILURE_MESSAGE": "No Heap Dump to delete.", + "ARIA_LABELS": { + "ROW_ACTION": "heap-dump-action-menu", + "SEARCH_INPUT": "heap-dump-search-input" } }, "ThreadDumps": { diff --git a/src/app/Dashboard/Diagnostics/DiagnosticsCard.tsx b/src/app/Dashboard/Diagnostics/DiagnosticsCard.tsx index 21f7c6326..d22286e4c 100644 --- a/src/app/Dashboard/Diagnostics/DiagnosticsCard.tsx +++ b/src/app/Dashboard/Diagnostics/DiagnosticsCard.tsx @@ -58,7 +58,18 @@ export const DiagnosticsCard: DashboardCardFC = (props) => const notifications = React.useContext(NotificationsContext); const addSubscription = useSubscriptions(); const [running, setRunning] = React.useState(false); + const [heapDumpReady, setHeapDumpReady] = React.useState(false); const [threadDumpReady, setThreadDumpReady] = React.useState(false); + const [controlEnabled, setControlEnabled] = React.useState(false); + + React.useEffect(() => { + addSubscription( + serviceContext.target.target().subscribe({ + next: (target) => setControlEnabled(target != null ? target.agent : false), + error: () => setControlEnabled(false), + }), + ); + }, [addSubscription, serviceContext, setControlEnabled]); const handleError = React.useCallback( (kind, error) => { @@ -83,6 +94,30 @@ export const DiagnosticsCard: DashboardCardFC = (props) => ); }, [addSubscription, serviceContext.api, serviceContext.target, setThreadDumpReady]); + React.useEffect(() => { + addSubscription( + serviceContext.target + .target() + .pipe( + filter((target) => !!target), + first(), + concatMap(() => serviceContext.api.getHeapDumps()), + ) + .subscribe({ + next: (dumps) => (dumps.length > 0 ? setHeapDumpReady(true) : setHeapDumpReady(false)), + error: () => setHeapDumpReady(false), + }), + ); + }, [addSubscription, serviceContext.api, serviceContext.target, setHeapDumpReady]); + + React.useEffect(() => { + addSubscription( + serviceContext.notificationChannel.messages(NotificationCategory.HeapDumpUploaded).subscribe(() => { + setHeapDumpReady(true); + }), + ); + }, [addSubscription, serviceContext.notificationChannel, setHeapDumpReady]); + React.useEffect(() => { addSubscription( serviceContext.notificationChannel.messages(NotificationCategory.ThreadDumpSuccess).subscribe(() => { @@ -114,6 +149,18 @@ export const DiagnosticsCard: DashboardCardFC = (props) => ); }, [addSubscription, serviceContext.api, handleError, setRunning, t]); + const handleHeapDump = React.useCallback(() => { + setRunning(true); + addSubscription( + serviceContext.api.runHeapDump(true).subscribe({ + error: (err) => handleError(t('DiagnosticsCard.KINDS.HEAP_DUMP'), err), + complete: () => { + setRunning(false); + }, + }), + ); + }, [addSubscription, serviceContext.api, handleError, setRunning, t]); + const header = React.useMemo(() => { return ( {...props.actions || []}, hasNoOffset: false, className: undefined }}> @@ -181,6 +228,30 @@ export const DiagnosticsCard: DashboardCardFC = (props) => + + + + + + + +