| <!-- |
| 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> |