blob: e922bc2aa61cd4bbb14046cb314947f23e84f688 [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
********************************************************************************/
package org.eclipse.jifa.worker.route.heapdump;
import com.google.common.base.Strings;
import io.vertx.core.Future;
import org.eclipse.jifa.worker.route.ParamKey;
import org.eclipse.jifa.worker.route.RouteMeta;
import org.eclipse.jifa.worker.support.Analyzer;
import org.eclipse.jifa.worker.support.heapdump.HeapDumpSupport;
import org.eclipse.jifa.worker.support.heapdump.SnapshotContext;
import org.eclipse.jifa.worker.vo.heapdump.HeapObject;
import org.eclipse.jifa.worker.vo.heapdump.leak.Report;
import org.eclipse.mat.SnapshotException;
import org.eclipse.mat.internal.Messages;
import org.eclipse.mat.internal.snapshot.SnapshotQueryContext;
import org.eclipse.mat.internal.snapshot.inspections.Path2GCRootsQuery;
import org.eclipse.mat.query.Bytes;
import org.eclipse.mat.query.IResult;
import org.eclipse.mat.query.IResultPie;
import org.eclipse.mat.query.refined.RefinedResultBuilder;
import org.eclipse.mat.query.refined.RefinedTree;
import org.eclipse.mat.query.results.CompositeResult;
import org.eclipse.mat.query.results.TextResult;
import org.eclipse.mat.report.QuerySpec;
import org.eclipse.mat.report.SectionSpec;
import org.eclipse.mat.report.Spec;
import org.eclipse.mat.snapshot.ISnapshot;
import org.eclipse.mat.snapshot.model.IObject;
import java.util.ArrayList;
import java.util.List;
class LeakRoute extends HeapBaseRoute {
private static final String OVERVIEW_PATTERN = Messages.LeakHunterQuery_Overview;
private static final String PROBLEM_SUSPECT_PATTERN =
Messages.LeakHunterQuery_ProblemSuspect.substring(0, Messages.LeakHunterQuery_ProblemSuspect.indexOf("{0}"));
private static final String HIND_PATTERN =
Messages.LeakHunterQuery_Hint.substring(0, Messages.LeakHunterQuery_Hint.indexOf("{0}"));
private static final String DESC_PATTERN = Messages.LeakHunterQuery_Description;
private static final String SHORTEST_PATHS_PATTERN = Messages.LeakHunterQuery_ShortestPaths;
@RouteMeta(path = "/leak/report")
void report(Future<Report> future, @ParamKey("file") String file) throws Exception {
SnapshotContext context = Analyzer.getOrOpenSnapshotContext(file);
IResult result = context.leakReport().getResult();
Report report = new Report();
if (result instanceof TextResult) {
report.setInfo(((TextResult) result).getText());
} else if (result instanceof SectionSpec) {
report.setUseful(true);
SectionSpec sectionSpec = (SectionSpec) result;
report.setName(sectionSpec.getName());
List<Spec> specs = sectionSpec.getChildren();
for (int i = 0; i < specs.size(); i++) {
QuerySpec spec = (QuerySpec) specs.get(i);
String name = spec.getName();
if (Strings.isNullOrEmpty(name)) {
continue;
}
if (name.startsWith(OVERVIEW_PATTERN)) {
IResultPie irtPie = (IResultPie) spec.getResult();
List<? extends IResultPie.Slice> pieSlices = irtPie.getSlices();
List<Report.Slice> slices = new ArrayList<>();
for (IResultPie.Slice slice : pieSlices) {
slices.add(new Report.Slice(slice.getLabel(), HeapDumpSupport.fetchObjectId(slice.getContext()),
slice.getValue(), slice.getDescription()));
}
report.setSlices(slices);
} else if (name.startsWith(PROBLEM_SUSPECT_PATTERN) || name.startsWith(HIND_PATTERN)) {
Report.Record suspect = new Report.Record();
suspect.setIndex(i);
suspect.setName(name);
CompositeResult cr = (CompositeResult) spec.getResult();
List<CompositeResult.Entry> entries = cr.getResultEntries();
for (CompositeResult.Entry entry : entries) {
String entryName = entry.getName();
if (Strings.isNullOrEmpty(entryName)) {
IResult r = entry.getResult();
if (r instanceof QuerySpec && ((QuerySpec) r).getName().equals(SHORTEST_PATHS_PATTERN)) {
Path2GCRootsQuery.Tree tree = (Path2GCRootsQuery.Tree) ((QuerySpec) r).getResult();
RefinedResultBuilder builder = new RefinedResultBuilder(
new SnapshotQueryContext(context.getSnapshot()), tree);
RefinedTree rst = (RefinedTree) builder.build();
List<?> elements = rst.getElements();
List<Report.ShortestPath> paths = new ArrayList<>();
suspect.setPaths(paths);
for (Object row : elements) {
paths.add(buildPath(context.getSnapshot(), rst, row));
}
}
} else if ((entryName.startsWith(DESC_PATTERN) || entryName.startsWith(OVERVIEW_PATTERN))) {
TextResult desText = (TextResult) entry.getResult();
suspect.setDesc(desText.getText());
}
}
List<Report.Record> records = report.getRecords();
if (records == null) {
report.setRecords(records = new ArrayList<>());
}
records.add(suspect);
}
}
}
future.complete(report);
}
private Report.ShortestPath buildPath(ISnapshot snapshot, RefinedTree rst, Object row) throws SnapshotException {
Report.ShortestPath shortestPath = new Report.ShortestPath();
shortestPath.setLabel((String) rst.getColumnValue(row, 0));
shortestPath.setShallowSize(((Bytes) rst.getColumnValue(row, 1)).getValue());
shortestPath.setRetainedSize(((Bytes) rst.getColumnValue(row, 2)).getValue());
int objectId = rst.getContext(row).getObjectId();
shortestPath.setObjectId(objectId);
IObject object = snapshot.getObject(objectId);
shortestPath.setGCRoot(snapshot.isGCRoot(objectId));
shortestPath.setObjectType(HeapObject.Type.typeOf(object));
if (rst.hasChildren(row)) {
List<Report.ShortestPath> children = new ArrayList<>();
shortestPath.setChildren(children);
for (Object c : rst.getChildren(row)) {
children.add(buildPath(snapshot, rst, c));
}
}
return shortestPath;
}
}