| <!-- |
| 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 xmlns:v-clipboard="http://www.w3.org/1999/xhtml"> |
| <el-container style="height: 100%"> |
| <el-main style="padding: 0; height: 100%"> |
| <div style="height: 100%; position: relative;"> |
| <el-tabs class="firstTabs" value="inspectorObjectView" style="position:relative; height: 38%;"> |
| <el-tab-pane name="inspectorObjectView"> |
| <span slot="label"> |
| <i class="el-icon-search"/> |
| <span style="margin-left: 2px"> Inspector</span> |
| </span> |
| |
| <div style="margin-bottom: 5px"> |
| <el-input size="mini" |
| width="60%" |
| placeholder="Object Address" |
| class="input-with-select" |
| v-model="objectAddress" |
| @keyup.enter.native="searchByAddress" |
| clearable> |
| <el-button slot="append" :icon="searching ? 'el-icon-loading' : 'el-icon-search'" |
| :disabled="searching" |
| @click="searchByAddress"/> |
| </el-input> |
| </div> |
| |
| |
| <el-table v-loading="viewLoading" |
| :data="objectOverview" |
| :show-header="false" |
| :highlight-current-row="true" |
| stripe |
| :cell-style="cellStyle" |
| empty-text=" " |
| height="80%" |
| > |
| <el-table-column align="left" show-overflow-tooltip> |
| <template slot-scope="scope"> |
| <p style="font-size: 12px; margin: 0 auto;"> |
| <img :src="scope.row.icon"/> {{ scope.row.data}} |
| <span style="font-weight: bold; color: #909399" |
| v-if="scope.row.suffix">{{ " " + scope.row.suffix}}</span> |
| <i v-if="scope.row.copyable" class="el-icon-document-copy copy-icon" |
| style="padding-left: 5px" |
| v-clipboard:copy="scope.row.data"/> |
| </p> |
| </template> |
| </el-table-column> |
| </el-table> |
| </el-tab-pane> |
| </el-tabs> |
| |
| <el-tabs :value="activeName" |
| class="bottomTabs" style="position: absolute; top: 38%; left: 0; right: 0; bottom: 0;"> |
| <el-tab-pane label="Attributes" name="Attributes"> |
| <Fields :file="file" :object-id="objectId"/> |
| </el-tab-pane> |
| <el-tab-pane label="Statics" name="Statics"> |
| <div style="height: 100%"> |
| <Fields :file="file" :object-id="objectId" static/> |
| </div> |
| </el-tab-pane> |
| <!-- <el-tab-pane label="Class Hierarchy" name="Class Hierarchy" disabled>--> |
| <!-- </el-tab-pane>--> |
| <el-tab-pane label="Value" name="Value"> |
| <div v-loading="valueLoading"> |
| <el-input |
| v-model="objectValue" |
| type="textarea" |
| style="font-size: 12px" |
| readonly |
| autosize> |
| </el-input> |
| </div> |
| </el-tab-pane> |
| </el-tabs> |
| </div> |
| </el-main> |
| </el-container> |
| </template> |
| <script> |
| |
| import axios from 'axios' |
| import {heapDumpService} from '../../util' |
| import {getIcon, ICONS} from './IconHealper'; |
| import {OBJECT_TYPE} from './CommonType'; |
| import Fields from './Fields' |
| |
| export default { |
| components: {Fields}, |
| props: ['file', 'objectId'], |
| data() { |
| return { |
| objectOverview: [], |
| activeName: 'Attributes', |
| cellStyle: {padding: 0}, |
| objectValue: '', |
| objectAddress: '', |
| searching: false, |
| viewLoading: false, |
| valueLoading: false, |
| } |
| }, |
| methods: { |
| locationDesc(locationType) { |
| if (locationType === 1) { |
| return "Young" |
| } |
| |
| if (locationType === 2) { |
| return "Old" |
| } |
| return "" |
| }, |
| searchByAddress() { |
| if (this.objectAddress) { |
| this.searching = true |
| let address = this.objectAddress |
| if (address.startsWith('0x') || address.startsWith('0X')) { |
| address = address.substring(2) |
| } |
| address = parseInt(address, 16); |
| axios.get(heapDumpService(this.file, "inspector/addressToId"), { |
| params: { |
| objectAddress: address |
| } |
| }).then(resp => { |
| this.$emit('setSelectedObjectId', resp.data) |
| this.$emit('outgoingRefsOfObj', resp.data, this.objectAddress) |
| this.searching = false |
| }).catch(() => { |
| this.objectAddress = null |
| this.searching = false |
| }) |
| } |
| }, |
| inspect() { |
| if (!this.objectId) { |
| return |
| } |
| |
| this.viewLoading = true |
| axios.get(heapDumpService(this.file, "inspector/objectView"), { |
| params: { |
| objectId: this.objectId |
| } |
| }).then(resp => { |
| let ov = resp.data |
| |
| let oa = '0x' + ov.objectAddress.toString(16) |
| |
| let tmpView = [] |
| tmpView.push({data: oa, icon: ICONS.id, suffix: this.locationDesc(ov.locationType), copyable: true}) |
| let name = ov.name |
| let p = '' |
| let dotPos = name.lastIndexOf(".") |
| if (dotPos >= 0) { |
| p = name.substring(0, dotPos) |
| name = name.substring(dotPos + 1) |
| } |
| tmpView.push({data: name, icon: getIcon(ov.gCRoot, ov.objectType)}) |
| tmpView.push({data: p, icon: ICONS.objects.class_package}) |
| |
| tmpView.push({ |
| data: ov.classLabel, |
| icon: getIcon(ov.classGCRoot, OBJECT_TYPE.CLASS) |
| }) |
| |
| tmpView.push({data: ov.superClassName ? ov.superClassName : ' ', icon: ICONS.objects.superclass}) |
| |
| tmpView.push({ |
| data: ov.classLoaderLabel, |
| icon: getIcon(ov.classLoaderGCRoot, OBJECT_TYPE.CLASSLOADER) |
| }) |
| tmpView.push({data: ov.shallowSize + ' (shallow size)', icon: ICONS.size}) |
| tmpView.push({data: ov.retainedSize + ' (retained size)', icon: ICONS.size}) |
| tmpView.push({data: ov.gcRootInfo, icon: ICONS.decorations.gc_root}) |
| this.objectOverview = tmpView |
| let maxContent = '' |
| tmpView.forEach(v => { |
| if (v.data.length > maxContent.length) { |
| maxContent = v.data |
| } |
| }) |
| if (ov.objectType === OBJECT_TYPE.CLASS) { |
| this.activeName = 'Statics' |
| } else { |
| this.activeName = 'Attributes' |
| } |
| this.viewLoading = false |
| }).catch(() => { |
| this.objectOverview = [] |
| this.viewLoading = false |
| }) |
| |
| this.valueLoading = true |
| axios.get(heapDumpService(this.file, "inspector/value"), { |
| params: { |
| objectId: this.objectId |
| } |
| }).then(resp => { |
| this.objectValue = resp.data |
| this.valueLoading = false |
| }).catch(() => { |
| this.valueLoading = false |
| }) |
| }, |
| }, |
| watch: { |
| objectId(id) { |
| if (id >= 0) { |
| this.inspect() |
| } else { |
| this.objectOverview = [] |
| } |
| }, |
| }, |
| mounted() { |
| this.inspect() |
| } |
| } |
| </script> |
| |
| <style scoped> |
| .copy-icon:hover { |
| color: #409EFF; |
| cursor: pointer; |
| } |
| |
| .firstTabs /deep/ .el-tabs__content { |
| position: absolute; |
| top: 45px; |
| left: 0; |
| right: 0; |
| bottom: 0; |
| overflow: scroll; |
| } |
| |
| .firstTabs /deep/ .el-tab-pane { |
| position: absolute; |
| top: 0; |
| left: 0; |
| right: 0; |
| bottom: 0; |
| overflow: scroll; |
| } |
| |
| .bottomTabs /deep/ .el-tabs__content { |
| position: absolute; |
| top: 45px; |
| left: 0; |
| right: 0; |
| bottom: 0; |
| overflow: scroll; |
| } |
| |
| .bottomTabs /deep/ .el-tab-pane { |
| position: absolute; |
| top: 0; |
| left: 0; |
| right: 0; |
| bottom: 0; |
| overflow: scroll; |
| } |
| </style> |