blob: e0ad45428ec59d261411d8226787ebb666fef29c [file] [log] [blame]
<!--
Copyright (c) 2020 Contributors to the Eclipse Foundation
See the NOTICE file(s) distributed with this work for additional
information regarding copyright ownership.
This program and the accompanying materials are made available under the
terms of the Eclipse Public License 2.0 which is available at
http://www.eclipse.org/legal/epl-2.0
SPDX-License-Identifier: EPL-2.0
-->
<!--suppress HtmlUnknownTag -->
<template>
<el-container style="height: 100%">
<el-header>
<view-menu subject="analysisResult"
:file="file" :analysisState="analysisState" :type="type" :showInspector="showInspector"
@setShowInspector="setShowInspector"
@expandResultDivWidth="expandResultDivWidth"
@shrinkResultDivWidth="shrinkResultDivWidth"
@resetResultDivWidth="resetResultDivWidth"/>
</el-header>
<el-main style="padding-top: 0; padding-bottom: 0; height: 100%">
<el-dialog
:title="$t('jifa.options')"
width="30%"
:visible.sync="optionViewVisible"
:close-on-press-escape=false :close-on-click-modal=false :show-close=false
append-to-body
modal>
<div>
<div>
<el-checkbox v-model="options.keepUnreachableObjects">Keep unreachable objects</el-checkbox>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="analyzeHeapDump" round>{{$t('jifa.confirm')}}</el-button>
</span>
</el-dialog>
<div style="padding-top: 20px" v-if="analysisState === 'IN_PROGRESS' || analysisState === 'ERROR'">
<b-progress height="2rem" show-progress :precision="2"
:value="progress"
:variant="progressState"
striped
:animated="progress < 100"/>
<b-card class="mt-3" bg-variant="dark" text-variant="white" v-if="message">
<b-card-text style="white-space: pre-line;">{{message}}</b-card-text>
<div class="d-flex justify-content-center mb-3" v-if="progressState === 'info'">
<b-spinner/>
</div>
</b-card>
</div>
<el-container v-if="analysisState === 'SUCCESS'" style="height: 100%">
<el-main style="padding: 5px; height: 100%">
<el-row :gutter="5" style="height: 100%">
<el-col :span="showInspector ? 19 : 24" style="height: 100%">
<el-tabs class="mainTabs" v-model="activeTab" tab-position="left" :before-leave="switchTab">
<el-tab-pane name="overview">
<span slot="label">{{$t('jifa.heap.overview')}}</span>
<overview :file="file"
@setGenerationInfoAvailable="setGenerationInfoAvailable"
@outgoingRefsOfObj="outgoingRefsOfObj"
@incomingRefsOfObj="incomingRefsOfObj"
@outgoingRefsOfClass="outgoingRefsOfClass"
@incomingRefsOfClass="incomingRefsOfClass"
@pathToGCRootsOfObj="pathToGCRootsOfObj"
@setSelectedObjectId="setSelectedObjectId"
/>
</el-tab-pane>
<el-tab-pane name="leakSuspects" lazy>
<span slot="label">{{$t('jifa.heap.leakSuspects')}}</span>
<leak-suspects :file="file"
@outgoingRefsOfObj="outgoingRefsOfObj"
@incomingRefsOfObj="incomingRefsOfObj"
@setSelectedObjectId="setSelectedObjectId"/>
</el-tab-pane>
<el-tab-pane name="GCRoots" lazy>
<span slot="label">{{$t('jifa.heap.GCRoots')}}</span>
<div v-bind:style="{ 'height': '100%', 'width': resultDivWidth}">
<GCRoots :file="file"
@outgoingRefsOfObj="outgoingRefsOfObj"
@incomingRefsOfObj="incomingRefsOfObj"
@outgoingRefsOfClass="outgoingRefsOfClass"
@incomingRefsOfClass="incomingRefsOfClass"
@pathToGCRootsOfObj="pathToGCRootsOfObj"
@setSelectedObjectId="setSelectedObjectId"/>
</div>
</el-tab-pane>
<el-tab-pane name="dominatorTree" lazy>
<span slot="label"> {{$t('jifa.heap.dominatorTree')}}</span>
<div v-bind:style="{ 'height': '100%', 'width': resultDivWidth}">
<dominator-tree :file="file"
@outgoingRefsOfObj="outgoingRefsOfObj"
@incomingRefsOfObj="incomingRefsOfObj"
@outgoingRefsOfClass="outgoingRefsOfClass"
@incomingRefsOfClass="incomingRefsOfClass"
@pathToGCRootsOfObj="pathToGCRootsOfObj"
@setSelectedObjectId="setSelectedObjectId"/>
</div>
</el-tab-pane>
<el-tab-pane name="histogram" lazy>
<span slot="label"> {{$t('jifa.heap.histogram')}}</span>
<div v-bind:style="{ 'height': '100%', 'width': resultDivWidth}">
<histogram :file="file" :generationInfoAvailable="generationInfoAvailable"
@outgoingRefsOfObj="outgoingRefsOfObj"
@incomingRefsOfObj="incomingRefsOfObj"
@outgoingRefsOfClass="outgoingRefsOfClass"
@incomingRefsOfClass="incomingRefsOfClass"
@pathToGCRootsOfObj="pathToGCRootsOfObj"
@setSelectedObjectId="setSelectedObjectId"/>
</div>
</el-tab-pane>
<el-tab-pane name="unreachableObjects" lazy>
<span slot="label"> {{$t('jifa.heap.unreachableObjects')}}</span>
<div v-bind:style="{ 'height': '100%', 'width': resultDivWidth}">
<unreachable-objects :file="file"
@setSelectedObjectId="setSelectedObjectId"/>
</div>
</el-tab-pane>
<el-tab-pane name="duplicatedClasses" lazy>
<span slot="label"> {{$t('jifa.heap.duplicatedClasses')}}</span>
<div v-bind:style="{ 'height': '100%', 'width': resultDivWidth}">
<duplicated-classes :file="file"
@setSelectedObjectId="setSelectedObjectId"/>
</div>
</el-tab-pane>
<el-tab-pane name="classLoaders" lazy>
<span slot="label"> {{$t('jifa.heap.classLoaders')}}</span>
<div v-bind:style="{ 'height': '100%', 'width': resultDivWidth}">
<class-loaders :file="file"
@setSelectedObjectId="setSelectedObjectId"
@outgoingRefsOfObj="outgoingRefsOfObj"
@incomingRefsOfObj="incomingRefsOfObj"
@outgoingRefsOfClass="outgoingRefsOfClass"
@incomingRefsOfClass="incomingRefsOfClass"
@pathToGCRootsOfObj="pathToGCRootsOfObj"/>
</div>
</el-tab-pane>
<el-tab-pane name="directByteBuffer" lazy>
<span slot="label"> {{$t('jifa.heap.directByteBuffer')}}</span>
<div v-bind:style="{ 'height': '100%', 'width': resultDivWidth}">
<direct-byte-buffer :file="file"
@setSelectedObjectId="setSelectedObjectId"
@pathToGCRootsOfObj="pathToGCRootsOfObj"/>
</div>
</el-tab-pane>
<el-tab-pane name="systemProperty" lazy>
<span slot="label"> {{$t('jifa.heap.systemProperty')}}</span>
<system-property :file="file"/>
</el-tab-pane>
<el-tab-pane name="thread" lazy>
<span slot="label"> {{$t('jifa.heap.threadInfo')}}</span>
<div v-bind:style="{ 'height': '100%', 'width': resultDivWidth}">
<thread :file="file" @setSelectedObjectId="setSelectedObjectId"/>
</div>
</el-tab-pane>
<el-tab-pane name="OQL" lazy>
<span slot="label"> OQL </span>
<div v-bind:style="{ 'height': '100%', 'width': resultDivWidth}">
<OQL :file="file"
@outgoingRefsOfObj="outgoingRefsOfObj"
@incomingRefsOfObj="incomingRefsOfObj"
@outgoingRefsOfClass="outgoingRefsOfClass"
@incomingRefsOfClass="incomingRefsOfClass"
@pathToGCRootsOfObj="pathToGCRootsOfObj"
@setSelectedObjectId="setSelectedObjectId"/>
</div>
</el-tab-pane>
<el-tab-pane name="HeapFileCompare" lazy v-if="$jifa.dev()">
<span slot="label">{{ $t("jifa.heap.compare") }}</span>
<heap-file-compare :file="file"/>
</el-tab-pane>
<el-tab-pane name="dynamicResultSlot" :disabled="!showDynamicResultSlot" class="dynamicTab">
<span slot="label"> <i class="el-icon-more-outline"/> </span>
<div v-bind:style="{ 'height': '100%', 'width': resultDivWidth}">
<dynamic-result-slot ref="dynamicResultSlot" :file="file"
@disableShowDynamicResultSlot="disableShowDynamicResultSlot"
@setSelectedObjectId="setSelectedObjectId"
@outgoingRefsOfObj="outgoingRefsOfObj"
@incomingRefsOfObj="incomingRefsOfObj"
@outgoingRefsOfClass="outgoingRefsOfClass"
@incomingRefsOfClass="incomingRefsOfClass"
@pathToGCRootsOfObj="pathToGCRootsOfObj"/>
</div>
</el-tab-pane>
</el-tabs>
</el-col>
<el-col :span="showInspector ? 5 : 0" style="height: 100%">
<Inspector :file="file"
:objectId="selectedObjectId"
@outgoingRefsOfObj="outgoingRefsOfObj"
@setSelectedObjectId="setSelectedObjectId"
v-if="showInspector"/>
</el-col>
</el-row>
</el-main>
</el-container>
</el-main>
<el-footer>
<Footer/>
</el-footer>
</el-container>
</template>
<script>
import axios from 'axios'
import {heapDumpService} from '../../util'
import Footer from "../footer"
import Overview from './Overview'
import Inspector from './Inspector'
import LeakSuspects from './LeakSuspects'
import ViewMenu from "../menu/ViewMenu"
import SystemProperty from "./SystemProperty"
import Thread from "./Thread";
import Histogram from "./Histogram"
import DuplicatedClasses from "./DuplicatedClasses"
import OQL from "./OQL"
import DynamicResultSlot from "./DynamicResultSlot"
import DominatorTree from "./DominatorTree"
import GCRoots from "./GCRoots"
import UnreachableObjects from './UnreachableObjects'
import ClassLoaders from './ClassLoaders'
import DirectByteBuffer from './DirectByteBuffer'
import HeapFileCompare from './HeapFileCompare'
export default {
props: ['file'],
data() {
return {
type: 'HEAP_DUMP',
optionViewVisible: false,
options: {
keepUnreachableObjects: true,
},
progress: 0,
progressState: 'info',
message: '',
pollingInternal: 1000,
analysisState: 'NOT_STARTED',
activeTab: 'overview',
oriActiveTab: null,
generationInfoAvailable: false,
selectedObjectId: null,
showDynamicResultSlot: false,
showInspector: true,
resultDivWidth: '100%'
}
},
components: {
ViewMenu,
Inspector,
Overview,
LeakSuspects,
GCRoots,
DominatorTree,
DuplicatedClasses,
Histogram,
Thread,
SystemProperty,
OQL,
DynamicResultSlot,
UnreachableObjects,
ClassLoaders,
DirectByteBuffer,
HeapFileCompare,
Footer
},
methods: {
expandResultDivWidth() {
let width = parseInt(this.resultDivWidth.substr(0, this.resultDivWidth.length - 1));
width += 7;
this.resultDivWidth = width + "%";
},
shrinkResultDivWidth() {
let width = parseInt(this.resultDivWidth.substr(0, this.resultDivWidth.length - 1));
if (width === 100) {
return;
}
width -= 7;
this.resultDivWidth = width + "%";
},
resetResultDivWidth() {
this.resultDivWidth = '100%';
},
switchTab(active, old) {
if (old !== 'dynamicResultSlot') {
this.oriActiveTab = old;
}
},
setGenerationInfoAvailable(ava) {
this.generationInfoAvailable = ava;
},
setShowDynamicResultSlot(val) {
this.showDynamicResultSlot = val;
if (val) {
this.oriActiveTab = this.activeTab;
this.activeTab = 'dynamicResultSlot';
} else if (this.activeTab === 'dynamicResultSlot') {
if (!this.oriActiveTab) {
this.oriActiveTab = 'overview';
}
this.activeTab = this.oriActiveTab;
}
},
enableShowDynamicResultSlot() {
this.setShowDynamicResultSlot(true);
},
disableShowDynamicResultSlot() {
this.setShowDynamicResultSlot(false);
},
setSelectedObjectId(id) {
this.selectedObjectId = id;
},
setShowInspector(val) {
this.showInspector = val;
},
outgoingRefsOfObj(id, label) {
this.$refs['dynamicResultSlot'].outgoingRefsOfObj(id, label);
this.enableShowDynamicResultSlot();
},
incomingRefsOfObj(id, label) {
this.$refs['dynamicResultSlot'].incomingRefsOfObj(id, label);
this.enableShowDynamicResultSlot();
},
outgoingRefsOfClass(id, label) {
this.$refs['dynamicResultSlot'].outgoingRefsOfClass(id, label);
this.enableShowDynamicResultSlot();
},
incomingRefsOfClass(id, label) {
this.$refs['dynamicResultSlot'].incomingRefsOfClass(id, label);
this.enableShowDynamicResultSlot();
},
pathToGCRootsOfObj(id, label) {
this.$refs['dynamicResultSlot'].pathToGCRootsOfObj(id, label);
this.enableShowDynamicResultSlot();
},
pollProgressOfAnalysis() {
let self = this;
if (!self || self._isDestroyed) {
return;
}
axios.get(heapDumpService(this.file, 'progressOfAnalysis')).then(resp => {
let state = resp.data.state;
let percent = resp.data.percent;
if (resp.data.message) {
this.message = resp.data.message.replace(/\\n/gm, "<b/>");
}
if (state === 'IN_PROGRESS') {
if (percent >= 1) {
this.progress = 99;
} else {
this.progress = percent * 100;
}
setTimeout(this.pollProgressOfAnalysis, this.pollingInternal)
} else if (state === 'SUCCESS') {
this.progress = 100;
this.progressState = 'success';
this.$notify({
title: this.$t("jifa.goToOverViewPrompt"),
position: 'top-right',
type: "success",
offset: 300,
duration: 1000,
showClose: true,
onClose: () => {
this.analysisState = "SUCCESS";
}
})
} else {
this.progressState = 'danger';
this.analysisState = "ERROR";
axios.post(heapDumpService(this.file, 'release'));
}
})
},
analyzeHeapDump() {
this.optionViewVisible = false;
let params = new FormData();
params.append('keep_unreachable_objects', this.options.keepUnreachableObjects);
this.doAnalyzeHeapDump(params);
},
doAnalyzeHeapDump(params) {
axios.post(heapDumpService(this.file, 'analyze'), new URLSearchParams(params)).then(() => {
this.analysisState = "IN_PROGRESS";
this.pollProgressOfAnalysis();
})
},
},
mounted() {
axios.get(heapDumpService(this.file, 'isFirstAnalysis')).then(resp => {
if (resp.data.result) {
this.optionViewVisible = true
} else {
this.doAnalyzeHeapDump(new FormData())
}
})
}
}
</script>
<style scoped>
.mainTabs {
height: 100%;
}
.mainTabs /deep/ .el-tabs__content {
height: 100%;
overflow: scroll;
}
.mainTabs /deep/ .el-tab-pane {
height: 100%;
overflow: scroll;
}
.mainTabs .dynamicTab /deep/ .el-tabs__content {
height: unset;
position: absolute;
top: 60px;
left: 0;
right: 0;
bottom: 0;
overflow: scroll;
}
.mainTabs .dynamicTab /deep/ .el-tab-pane {
height: unset;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: scroll;
}
</style>