blob: 1cbc3b2d1defb4359f151a8c07bb1573666abb8e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 2021 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.mat.tests.acquire;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.number.OrderingComparison.greaterThan;
import static org.hamcrest.number.OrderingComparison.greaterThanOrEqualTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeThat;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.eclipse.mat.SnapshotException;
import org.eclipse.mat.internal.acquire.HeapDumpProviderDescriptor;
import org.eclipse.mat.internal.acquire.HeapDumpProviderRegistry;
import org.eclipse.mat.query.IContextObject;
import org.eclipse.mat.query.IResult;
import org.eclipse.mat.query.IResultTree;
import org.eclipse.mat.snapshot.ISnapshot;
import org.eclipse.mat.snapshot.SnapshotFactory;
import org.eclipse.mat.snapshot.acquire.IHeapDumpProvider;
import org.eclipse.mat.snapshot.acquire.VmInfo;
import org.eclipse.mat.snapshot.query.SnapshotQuery;
import org.eclipse.mat.tests.TestSnapshots;
import org.eclipse.mat.util.IProgressListener;
import org.eclipse.mat.util.VoidProgressListener;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ErrorCollector;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
/**
* Test the triggering and collection of heap dumps from other processes.
*/
@RunWith(value = Parameterized.class)
public class AcquireDumpTest
{
@Rule
public ErrorCollector collector = new ErrorCollector();
HeapDumpProviderDescriptor hdpd;
int num;
int nall;
private static int count;
private static int found;
public AcquireDumpTest(HeapDumpProviderDescriptor hdpd, String name, int id, int n)
{
this.hdpd = hdpd;
num = id;
nall = n;
}
@Parameters(name = "{1}")
public static Collection<Object[]> data()
{
List<Object[]> parms = new ArrayList<Object[]>();
Collection<HeapDumpProviderDescriptor> descs = HeapDumpProviderRegistry.instance().getHeapDumpProviders();
int id = 0;
for (HeapDumpProviderDescriptor hdpd : descs)
{
++id;
parms.add(new Object[] {hdpd, hdpd.getName(), id, descs.size()});
}
return parms;
}
/**
* Check there are JMap and IBM dump providers
*/
@Test
public void test()
{
Collection<HeapDumpProviderDescriptor> descs = HeapDumpProviderRegistry.instance().getHeapDumpProviders();
collector.checkThat("Should be HPROF and IBM Dumps descriptors", descs.size(), greaterThanOrEqualTo(2));
}
/**
* Check the providers have sensible properties
*/
@Test
public void test2()
{
HeapDumpProviderDescriptor hd = hdpd;
collector.checkThat("Should be some help", hd.getHelp().length(), greaterThan(20));
collector.checkThat("Should be a name", hd.getName().length(), greaterThan(3));
collector.checkThat("Locale", hd.getHelpLocale(), notNullValue());
// collector.checkThat("Icon", hd.getIcon(), notNullValue());
collector.checkThat("Should be an ID", hd.getIdentifier().length(), greaterThan(3));
IHeapDumpProvider hdp = hd.getHeapDumpProvider();
collector.checkThat("Heap Dump Provider", hdp, notNullValue());
collector.checkThat("Heap Dump Provider toString", hdp.toString(), notNullValue());
}
/**
* Actually generate a dump and parse it
*
* @throws SnapshotException
* @throws IOException
*/
public void test3(boolean compress, boolean chunked) throws SnapshotException, IOException
{
File tmpdir = TestSnapshots.createGeneratedName("acquire", null);
HeapDumpProviderDescriptor hd = hdpd;
do {
// Currently compressed HPROF is not too slow, but IBM is
if (compress)
assumeThat(hd.getName(), containsString("HPROF"));
IHeapDumpProvider hdp = hd.getHeapDumpProvider();
collector.checkThat("Heap Dump Provider", hdp, notNullValue());
IProgressListener l = new VoidProgressListener();
List<? extends VmInfo> ls;
try
{
ls = hdp.getAvailableVMs(l);
}
catch (SnapshotException e)
{
continue;
}
collector.checkThat("Heap dump provider "+hdp, ls, notNullValue());
if (ls == null)
continue;
for (VmInfo vm : ls)
{
incCount();
String desc = vm.getDescription();
collector.checkThat("VM description", desc, notNullValue());
if (desc.contains("org.eclipse.mat.tests"))
{
System.out.println("Desc " + desc);
// If we can, try compressing the dump to test more code paths
try
{
vm.getClass().getField("compress").set(vm, compress);
vm.getClass().getField("chunked").set(vm, chunked);
}
catch (NoSuchFieldException e1)
{
e1.printStackTrace();
}
catch (SecurityException e1)
{
e1.printStackTrace();
}
catch (IllegalArgumentException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
// If we can, try doing a GC before the dump to keep the size down
try
{
vm.getClass().getField("live").set(vm, true);
}
catch (NoSuchFieldException e1)
{
e1.printStackTrace();
}
catch (SecurityException e1)
{
e1.printStackTrace();
}
catch (IllegalArgumentException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
File f = new File(vm.getProposedFileName());
System.out.println("Proposed name "+f+" "+hdp);
String fname = f.getName();
int ldot = fname.endsWith(".gz") ?
fname.lastIndexOf('.', fname.length() - 4)
: fname.lastIndexOf('.');
String fname2 = "acquire_dump_" + (num) + fname.substring(ldot);
File tmpdump = new File(tmpdir, fname2);
System.out.println("Dump " + tmpdump);
File dmp;
try
{
dmp = hdp.acquireDump(vm, tmpdump, l);
}
catch (SnapshotException e)
{
if (e.getMessage().contains("Unsuitable target"))
{
// Java 9 cannot attach to itself
System.out.println("Ignoring dump as: "+e.getMessage());
continue;
}
else
{
throw e;
}
}
collector.checkThat("Dump file", dmp, notNullValue());
System.out.println("Dump "+dmp.getAbsolutePath()+" "+dmp.length());
Runtime rt = Runtime.getRuntime();
System.out.println("Max memory="+rt.maxMemory());
System.out.println("Total memory="+rt.totalMemory());
System.out.println("Free memory="+rt.freeMemory());
try
{
ISnapshot answer = SnapshotFactory.openSnapshot(dmp, Collections.<String, String> emptyMap(), l);
System.out.println("Snapshot path="+answer.getSnapshotInfo().getPath());
System.out.println("Max memory="+rt.maxMemory());
System.out.println("Total memory="+rt.totalMemory());
System.out.println("Free memory="+rt.freeMemory());
try
{
collector.checkThat("Snapshot", answer, notNullValue());
incFound();
// Currently zipped hprof is very slow (>1 hour)
if (!compress || chunked)
{
checkEclipseBundleQuery(answer);
}
}
finally
{
SnapshotFactory.dispose(answer);
answer = null;
}
}
finally
{
boolean del = dmp.delete();
if (!del) {
System.out.println("Unable to delete "+dmp);
}
}
}
}
} while (false);
// See if any of the tests with any provider actually loads a dump
if (num == nall)
{
collector.checkThat("Available VMs", getCount(), greaterThan(0));
collector.checkThat("Available dumps from VMs", getFound(), greaterThan(0));
}
}
/**
* Actually generate a dump and parse it
*
* @throws SnapshotException
* @throws IOException
*/
//@Ignore("OOM error with new CI build")
@Test
public void testAcquireDumpUncompressed() throws SnapshotException, IOException
{
test3(false, false);
}
/**
* Actually generate a dump and parse it
*
* @throws SnapshotException
* @throws IOException
*/
//@Ignore("OOM error with new CI build")
@Test
public void testAcquireDumpCompressed() throws SnapshotException, IOException
{
test3(true, false);
}
/**
* Actually generate a dump and parse it
*
* @throws SnapshotException
* @throws IOException
*/
//@Ignore("OOM error with new CI build")
@Test
public void testAcquireDumpCompressedChunked() throws SnapshotException, IOException
{
test3(true, true);
}
/**
* This query requires an heap dump from a running Eclipse system to
* test the Eclipse bundle query. Other test dumps from non-Eclipse programs won't work.
* @param snapshot
* @throws SnapshotException
*/
void checkEclipseBundleQuery(ISnapshot snapshot) throws SnapshotException
{
SnapshotQuery query = SnapshotQuery.parse("bundle_registry -groupby NONE", snapshot);
assertNotNull(query);
IResult result = query.execute(new VoidProgressListener());
assertNotNull(result);
IResultTree tree = (IResultTree) result;
int found = 0;
int f2 = 0;
for (Object o : tree.getElements())
{
IContextObject ctx = tree.getContext(o);
Object o2 = tree.getColumnValue(o, 0);
if (o2.toString().startsWith("org.eclipse.mat.tests "))
{
found++;
}
if (o2.toString().startsWith("org.eclipse.mat.api "))
{
List<?> l3 = tree.getChildren(o);
for (Object o3 : l3)
{
Object o4 = tree.getColumnValue(o3, 0);
if (o4.toString().startsWith("Dependencies"))
{
f2 |= 1;
checkSubtree(tree, o3, 3, "org.eclipse.mat.report ", "Expected dependencies of org.eclipse.mat.api to include");
}
if (o4.toString().startsWith("Dependents"))
{
f2 |= 2;
checkSubtree(tree, o3, 4, "org.eclipse.mat.parser ", "Expected dependendents of org.eclipse.mat.api to include");
}
if (o4.toString().startsWith("Extension Points"))
{
f2 |= 4;
checkSubtree(tree, o3, 7, "org.eclipse.mat.api.factory", "Expected extension points of org.eclipse.mat.api to include");
}
if (o4.toString().startsWith("Extensions"))
{
f2 |= 8;
checkSubtree(tree, o3, 7, "org.eclipse.mat.api.nameResolver", "Expected extensions of org.eclipse.mat.api to include");
}
if (o4.toString().startsWith("Used Services"))
{
f2 |= 16;
checkSubtree(tree, o3, 1, "org.eclipse.osgi.service.debug.DebugOptions", "Expected used services of org.eclipse.mat.api to include");
}
}
}
}
assertEquals("Expected to find a org.eclipse.mat.tests plugin", found, 1);
assertEquals("Expected Dependencies,Dependents, Extension Points, Extensions, Used Services from org.eclipse.mat.api", f2, 31);
}
private void checkSubtree(IResultTree tree, Object o3, int minElements, String toFind, String errMsg)
{
List<?> l5 = tree.getChildren(o3);
assertThat(errMsg + " " + l5.toString(), l5.size(), greaterThanOrEqualTo(minElements));
boolean foundItem = false;
for (Object o6 : l5)
{
Object o7 = tree.getColumnValue(o6, 0);
if (o7.toString().startsWith(toFind))
{
foundItem = true;
}
//System.out.println("Found "+o7+" "+errMsg);
}
assertTrue(errMsg+" "+toFind, foundItem);
}
static int getCount()
{
return count;
}
static void incCount()
{
++count;
}
static int getFound()
{
return found;
}
static void incFound()
{
++found;
}
}