| <!-- |
| 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 |
| --> |
| <template> |
| <el-table |
| ref="table" |
| :data="roots" |
| :highlight-current-row="false" |
| stripe |
| :header-cell-style="headerCellStyle" |
| :cell-style='cellStyle' |
| :span-method="spanMethod" |
| row-key="rowKey" |
| lazy |
| v-loading="loading" |
| height="100%" |
| :indent=8 |
| :load="load" |
| > |
| |
| <el-table-column label="Class Name" show-overflow-tooltip> |
| <template slot-scope="scope"> |
| <span v-if="scope.row.isRoot"> |
| <img :src="scope.row.icon" style="margin-right: 5px"/> |
| {{ scope.row.label }} |
| </span> |
| |
| <span v-if="scope.row.isClass" @click="$emit('setSelectedObjectId', scope.row.objectId)" |
| style="cursor: pointer"> |
| <img :src="scope.row.icon" style="margin-right: 5px"/> |
| {{ scope.row.label }} |
| </span> |
| |
| <span v-if="scope.row.isObject" @click="$emit('setSelectedObjectId', scope.row.objectId)" |
| style="cursor: pointer"> |
| <img :src="scope.row.icon" style="margin-right: 5px"/> |
| {{ scope.row.label }} |
| <!--<span style="font-weight: bold; color: #909399">--> |
| <!--{{ scope.row.suffix }}--> |
| <!--</span>--> |
| </span> |
| |
| <span v-if="scope.row.isOutbound" @click="$emit('setSelectedObjectId', scope.row.objectId)" |
| style="cursor: pointer"> |
| <img :src="scope.row.icon" style="margin-right: 5px"/> |
| <strong>{{ scope.row.prefix }}</strong> |
| {{ scope.row.label }} |
| <span style="font-weight: bold; color: #909399"> |
| {{ scope.row.suffix }} |
| </span> |
| </span> |
| |
| |
| <span v-if="scope.row.isClassSummary"> |
| <img :src="ICONS.misc.sumIcon" v-if="scope.row.currentSize >= scope.row.totalSize"/> |
| <img :src="ICONS.misc.sumPlusIcon" |
| @dblclick="fetchClasses(scope.row.parentRowKey, scope.row.rootTypeIndex, scope.row.nextPage, scope.row.resolve)" |
| style="cursor: pointer" v-else/> |
| {{ scope.row.currentSize}} <strong> / </strong> {{scope.row.totalSize}} |
| </span> |
| |
| <span v-if="scope.row.isObjectSummary"> |
| <img :src="ICONS.misc.sumIcon" v-if="scope.row.currentSize >= scope.row.totalSize"/> |
| <img :src="ICONS.misc.sumPlusIcon" |
| @dblclick="fetchObjects(scope.row.parentRowKey, scope.row.rootTypeIndex, |
| scope.row.classIndex, scope.row.nextPage, scope.row.resolve)" |
| style="cursor: pointer" v-else/> |
| {{ scope.row.currentSize}} <strong> / </strong> {{scope.row.totalSize}} |
| </span> |
| |
| <span v-if="scope.row.isOutboundsSummary"> |
| <img :src="ICONS.misc.sumIcon" v-if="scope.row.currentSize >= scope.row.totalSize"/> |
| <img :src="ICONS.misc.sumPlusIcon" |
| @dblclick="fetchOutbounds(scope.row.parentRowKey, scope.row.objectId, scope.row.nextPage, scope.row.resolve)" |
| style="cursor: pointer" |
| v-else/> |
| {{ scope.row.currentSize }} <strong> / </strong> {{ scope.row.totalSize }} |
| </span> |
| </template> |
| </el-table-column> |
| <el-table-column/> |
| <el-table-column/> |
| <el-table-column/> |
| <el-table-column/> |
| <el-table-column/> |
| <el-table-column/> |
| |
| <el-table-column label="Objects" prop="objects"> |
| </el-table-column> |
| |
| <el-table-column label="Shallow Heap" prop="shallowHeap"> |
| </el-table-column> |
| |
| <el-table-column label="Retained Heap" prop="retainedHeap"> |
| </el-table-column> |
| </el-table> |
| </template> |
| |
| <script> |
| import axios from 'axios' |
| import {getIcon, getOutboundIcon, ICONS} from "./IconHealper"; |
| import {heapDumpService} from '../../util' |
| |
| let rowKey = 1 |
| export default { |
| props: ['file'], |
| data() { |
| return { |
| cellStyle: {padding: '4px', fontSize: '12px'}, |
| headerCellStyle: {padding: 0, 'font-size': '12px', 'font-weight': 'normal'}, |
| ICONS, |
| pageSize: 25, |
| loading: false, |
| roots: [] |
| } |
| }, |
| |
| methods: { |
| spanMethod(row) { |
| let index = row.columnIndex |
| if (index === 0) { |
| return [1, 7] |
| } else if (index >= 1 && index <= 6) { |
| return [0, 0] |
| } |
| return [1, 1] |
| }, |
| fetchRoots() { |
| this.loading = true |
| axios.get(heapDumpService(this.file, 'GCRoots')).then(resp => { |
| let rootIndex = 0 |
| resp.data.forEach( |
| root => { |
| this.roots.push({ |
| rowKey: rowKey++, |
| isRoot: true, |
| rootTypeIndex: rootIndex++, |
| label: root.className, |
| objects: root.objects, |
| icon: ICONS.roots, |
| hasChildren: root.objects > 0, |
| }) |
| }, |
| this.loading = false |
| ) |
| }) |
| }, |
| fetchClasses(parentRowKey, rootTypeIndex, page, resolve) { |
| this.loading = true |
| axios.get(heapDumpService(this.file, 'GCRoots/classes'), { |
| params: { |
| rootTypeIndex: rootTypeIndex, |
| page: page, |
| pageSize: this.pageSize, |
| } |
| }).then(resp => { |
| let loadedLen = 0; |
| let loaded = this.$refs['table'].store.states.lazyTreeNodeMap[parentRowKey] |
| let callResolve = false |
| if (loaded) { |
| loadedLen = loaded.length |
| if (loadedLen > 0) { |
| loaded.splice(--loadedLen, 1) |
| } |
| } else { |
| loaded = [] |
| callResolve = true; |
| } |
| |
| let res = resp.data.data |
| let classIndex = loadedLen |
| res.forEach(d => { |
| loaded.push({ |
| rowKey: rowKey++, |
| icon: ICONS.objects.class, |
| label: d.className, |
| objects: d.objects, |
| objectId: d.objectId, |
| rootTypeIndex: rootTypeIndex, |
| classIndex: classIndex++, |
| isClass: true, |
| hasChildren: true, |
| }) |
| }) |
| |
| loaded.push({ |
| rowKey: rowKey++, |
| parentRowKey: parentRowKey, |
| isClassSummary: true, |
| rootTypeIndex: rootTypeIndex, |
| nextPage: page + 1, |
| currentSize: loadedLen + res.length, |
| totalSize: resp.data.totalSize, |
| resolve: resolve, |
| }) |
| |
| if (callResolve) { |
| resolve(loaded) |
| } |
| this.loading = false |
| }) |
| }, |
| |
| fetchObjects(parentRowKey, rootTypeIndex, classIndex, page, resolve) { |
| this.loading = true |
| axios.get(heapDumpService(this.file, 'GCRoots/class/objects'), { |
| params: { |
| rootTypeIndex: rootTypeIndex, |
| classIndex: classIndex, |
| page: page, |
| pageSize: this.pageSize, |
| } |
| }).then(resp => { |
| let loadedLen = 0; |
| let loaded = this.$refs['table'].store.states.lazyTreeNodeMap[parentRowKey] |
| let callResolve = false |
| if (loaded) { |
| loadedLen = loaded.length |
| if (loadedLen > 0) { |
| loaded.splice(--loadedLen, 1) |
| } |
| } else { |
| loaded = [] |
| callResolve = true; |
| } |
| |
| let res = resp.data.data |
| res.forEach(d => { |
| loaded.push({ |
| rowKey: rowKey++, |
| icon: getIcon(d.gCRoot, d.objectType), |
| label: d.label, |
| objectId: d.objectId, |
| suffix: d.suffix, |
| shallowHeap: d.shallowSize, |
| retainedHeap: d.retainedSize, |
| isObject: true, |
| hasChildren: true, |
| }) |
| }) |
| |
| loaded.push({ |
| rowKey: rowKey++, |
| parentRowKey: parentRowKey, |
| isObjectSummary: true, |
| rootTypeIndex: rootTypeIndex, |
| classIndex: classIndex, |
| nextPage: page + 1, |
| currentSize: loadedLen + res.length, |
| totalSize: resp.data.totalSize, |
| resolve: resolve, |
| }) |
| |
| if (callResolve) { |
| resolve(loaded) |
| } |
| this.loading = false |
| }) |
| }, |
| |
| fetchOutbounds(parentRowKey, objectId, page, resolve) { |
| this.loading = true |
| axios.get(heapDumpService(this.file, 'outbounds'), { |
| params: { |
| objectId: objectId, |
| page: page, |
| pageSize: this.pageSize, |
| } |
| }).then(resp => { |
| let loadedLen = 0; |
| let loaded = this.$refs['table'].store.states.lazyTreeNodeMap[parentRowKey] |
| let callResolve = false |
| if (loaded) { |
| loadedLen = loaded.length |
| if (loadedLen > 0) { |
| loaded.splice(--loadedLen, 1) |
| } |
| } else { |
| loaded = [] |
| callResolve = true; |
| } |
| |
| let res = resp.data.data |
| res.forEach(d => { |
| loaded.push({ |
| rowKey: rowKey++, |
| icon: getOutboundIcon(d.gCRoot, d.objectType), |
| prefix: d.prefix, |
| label: d.label, |
| suffix: d.suffix, |
| shallowHeap: d.shallowSize, |
| retainedHeap: d.retainedSize, |
| hasChildren: d.hasOutbound, |
| objectId: d.objectId, |
| isOutbound: true |
| }) |
| }) |
| |
| loaded.push({ |
| rowKey: rowKey++, |
| objectId: objectId, |
| parentRowKey: parentRowKey, |
| isOutboundsSummary: true, |
| nextPage: page + 1, |
| currentSize: loadedLen + res.length, |
| totalSize: resp.data.totalSize, |
| resolve: resolve, |
| }) |
| |
| if (callResolve) { |
| resolve(loaded) |
| } |
| this.loading = false |
| }) |
| }, |
| |
| load(tree, treeNode, resolve) { |
| if (tree.isRoot) { |
| this.fetchClasses(tree.rowKey, tree.rootTypeIndex, 1, resolve) |
| } else if (tree.isClass) { |
| this.fetchObjects(tree.rowKey, tree.rootTypeIndex, tree.classIndex, 1, resolve) |
| } else { |
| this.fetchOutbounds(tree.rowKey, tree.objectId, 1, resolve) |
| } |
| } |
| }, |
| |
| created() { |
| this.fetchRoots() |
| } |
| } |
| </script> |