/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import java.io.File; import java.io.IOException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.ArrayList; import java.util.Map; public class Main { private static final String TEMP_FILE_NAME_PREFIX = "test"; private static final String TEMP_FILE_NAME_SUFFIX = ".trace"; public static void main(String[] args) throws Exception { String name = System.getProperty("java.vm.name"); if (!"Dalvik".equals(name)) { System.out.println("This test is not supported on " + name); return; } testMethodTracing(); testCountInstances(); testRuntimeStat(); testRuntimeStats(); } private static File createTempFile() throws Exception { try { return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX); } catch (IOException e) { System.setProperty("java.io.tmpdir", "/data/local/tmp"); try { return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX); } catch (IOException e2) { System.setProperty("java.io.tmpdir", "/sdcard"); return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX); } } } private static void testMethodTracing() throws Exception { File tempFile = null; try { tempFile = createTempFile(); testMethodTracingToFile(tempFile); } finally { if (tempFile != null) { tempFile.delete(); } } } private static void testMethodTracingToFile(File tempFile) throws Exception { String tempFileName = tempFile.getPath(); if (VMDebug.getMethodTracingMode() != 0) { VMDebug.stopMethodTracing(); } System.out.println("Confirm enable/disable"); System.out.println("status=" + VMDebug.getMethodTracingMode()); VMDebug.startMethodTracing(tempFileName, 0, 0, false, 0); System.out.println("status=" + VMDebug.getMethodTracingMode()); VMDebug.stopMethodTracing(); System.out.println("status=" + VMDebug.getMethodTracingMode()); if (tempFile.length() == 0) { System.out.println("ERROR: tracing output file is empty"); } System.out.println("Confirm sampling"); VMDebug.startMethodTracing(tempFileName, 0, 0, true, 1000); System.out.println("status=" + VMDebug.getMethodTracingMode()); VMDebug.stopMethodTracing(); System.out.println("status=" + VMDebug.getMethodTracingMode()); if (tempFile.length() == 0) { System.out.println("ERROR: sample tracing output file is empty"); } System.out.println("Test starting when already started"); VMDebug.startMethodTracing(tempFileName, 0, 0, false, 0); System.out.println("status=" + VMDebug.getMethodTracingMode()); VMDebug.startMethodTracing(tempFileName, 0, 0, false, 0); System.out.println("status=" + VMDebug.getMethodTracingMode()); System.out.println("Test stopping when already stopped"); VMDebug.stopMethodTracing(); System.out.println("status=" + VMDebug.getMethodTracingMode()); VMDebug.stopMethodTracing(); System.out.println("status=" + VMDebug.getMethodTracingMode()); System.out.println("Test tracing with empty filename"); try { VMDebug.startMethodTracing("", 0, 0, false, 0); System.out.println("Should have thrown an exception"); } catch (Exception e) { System.out.println("Got expected exception"); } System.out.println("Test tracing with bogus (< 1024 && != 0) filesize"); try { VMDebug.startMethodTracing(tempFileName, 1000, 0, false, 0); System.out.println("Should have thrown an exception"); } catch (Exception e) { System.out.println("Got expected exception"); } System.out.println("Test sampling with bogus (<= 0) interval"); try { VMDebug.startMethodTracing(tempFileName, 0, 0, true, 0); System.out.println("Should have thrown an exception"); } catch (Exception e) { System.out.println("Got expected exception"); } tempFile.delete(); } private static void checkNumber(String s) throws Exception { if (s == null) { System.out.println("Got null string"); return; } long n = Long.parseLong(s); if (n < 0) { System.out.println("Got negative number " + n); } } private static void checkHistogram(String s) throws Exception { if (s == null || s.length() == 0) { System.out.println("Got null or empty string"); return; } String[] buckets = s.split(","); long last_key = 0; for (int i = 0; i < buckets.length; ++i) { String bucket = buckets[i]; if (bucket.length() == 0) { System.out.println("Got empty bucket"); continue; } String[] kv = bucket.split(":"); if (kv.length != 2 || kv[0].length() == 0 || kv[1].length() == 0) { System.out.println("Got bad bucket " + bucket); continue; } long key = Long.parseLong(kv[0]); long value = Long.parseLong(kv[1]); if (key < 0 || value < 0) { System.out.println("Got negative key or value " + bucket); continue; } if (key < last_key) { System.out.println("Got decreasing key " + bucket); continue; } last_key = key; } } private static void testRuntimeStat() throws Exception { // Invoke at least one GC and wait for 20 seconds or so so we get at // least one bucket in the histograms. for (int i = 0; i < 20; ++i) { Runtime.getRuntime().gc(); Thread.sleep(1000L); } String gc_count = VMDebug.getRuntimeStat("art.gc.gc-count"); String gc_time = VMDebug.getRuntimeStat("art.gc.gc-time"); String bytes_allocated = VMDebug.getRuntimeStat("art.gc.bytes-allocated"); String bytes_freed = VMDebug.getRuntimeStat("art.gc.bytes-freed"); String blocking_gc_count = VMDebug.getRuntimeStat("art.gc.blocking-gc-count"); String blocking_gc_time = VMDebug.getRuntimeStat("art.gc.blocking-gc-time"); String gc_count_rate_histogram = VMDebug.getRuntimeStat("art.gc.gc-count-rate-histogram"); String blocking_gc_count_rate_histogram = VMDebug.getRuntimeStat("art.gc.blocking-gc-count-rate-histogram"); checkNumber(gc_count); checkNumber(gc_time); checkNumber(bytes_allocated); checkNumber(bytes_freed); checkNumber(blocking_gc_count); checkNumber(blocking_gc_time); checkHistogram(gc_count_rate_histogram); checkHistogram(blocking_gc_count_rate_histogram); } private static void testRuntimeStats() throws Exception { // Invoke at least one GC and wait for 20 seconds or so so we get at // least one bucket in the histograms. for (int i = 0; i < 20; ++i) { Runtime.getRuntime().gc(); Thread.sleep(1000L); } Map<String, String> map = VMDebug.getRuntimeStats(); String gc_count = map.get("art.gc.gc-count"); String gc_time = map.get("art.gc.gc-time"); String bytes_allocated = map.get("art.gc.bytes-allocated"); String bytes_freed = map.get("art.gc.bytes-freed"); String blocking_gc_count = map.get("art.gc.blocking-gc-count"); String blocking_gc_time = map.get("art.gc.blocking-gc-time"); String gc_count_rate_histogram = map.get("art.gc.gc-count-rate-histogram"); String blocking_gc_count_rate_histogram = map.get("art.gc.blocking-gc-count-rate-histogram"); checkNumber(gc_count); checkNumber(gc_time); checkNumber(bytes_allocated); checkNumber(bytes_freed); checkNumber(blocking_gc_count); checkNumber(blocking_gc_time); checkHistogram(gc_count_rate_histogram); checkHistogram(blocking_gc_count_rate_histogram); } static class ClassA { } static class ClassB { } static class ClassC extends ClassA { } private static void testCountInstances() throws Exception { ArrayList<Object> l = new ArrayList<Object>(); l.add(new ClassA()); l.add(new ClassB()); l.add(new ClassA()); l.add(new ClassC()); Runtime.getRuntime().gc(); System.out.println("Instances of ClassA " + VMDebug.countInstancesofClass(ClassA.class, false)); System.out.println("Instances of ClassB " + VMDebug.countInstancesofClass(ClassB.class, false)); System.out.println("Instances of null " + VMDebug.countInstancesofClass(null, false)); System.out.println("Instances of ClassA assignable " + VMDebug.countInstancesofClass(ClassA.class, true)); Class<?>[] classes = new Class<?>[] {ClassA.class, ClassB.class, null}; long[] counts = VMDebug.countInstancesofClasses(classes, false); System.out.println("Array counts " + Arrays.toString(counts)); counts = VMDebug.countInstancesofClasses(classes, true); System.out.println("Array counts assignable " + Arrays.toString(counts)); } private static class VMDebug { private static final Method startMethodTracingMethod; private static final Method stopMethodTracingMethod; private static final Method getMethodTracingModeMethod; private static final Method getRuntimeStatMethod; private static final Method getRuntimeStatsMethod; private static final Method countInstancesOfClassMethod; private static final Method countInstancesOfClassesMethod; static { try { Class<?> c = Class.forName("dalvik.system.VMDebug"); startMethodTracingMethod = c.getDeclaredMethod("startMethodTracing", String.class, Integer.TYPE, Integer.TYPE, Boolean.TYPE, Integer.TYPE); stopMethodTracingMethod = c.getDeclaredMethod("stopMethodTracing"); getMethodTracingModeMethod = c.getDeclaredMethod("getMethodTracingMode"); getRuntimeStatMethod = c.getDeclaredMethod("getRuntimeStat", String.class); getRuntimeStatsMethod = c.getDeclaredMethod("getRuntimeStats"); countInstancesOfClassMethod = c.getDeclaredMethod("countInstancesOfClass", Class.class, Boolean.TYPE); countInstancesOfClassesMethod = c.getDeclaredMethod("countInstancesOfClasses", Class[].class, Boolean.TYPE); } catch (Exception e) { throw new RuntimeException(e); } } public static void startMethodTracing(String filename, int bufferSize, int flags, boolean samplingEnabled, int intervalUs) throws Exception { startMethodTracingMethod.invoke(null, filename, bufferSize, flags, samplingEnabled, intervalUs); } public static void stopMethodTracing() throws Exception { stopMethodTracingMethod.invoke(null); } public static int getMethodTracingMode() throws Exception { return (int) getMethodTracingModeMethod.invoke(null); } public static String getRuntimeStat(String statName) throws Exception { return (String) getRuntimeStatMethod.invoke(null, statName); } public static Map<String, String> getRuntimeStats() throws Exception { return (Map<String, String>) getRuntimeStatsMethod.invoke(null); } public static long countInstancesofClass(Class<?> c, boolean assignable) throws Exception { return (long) countInstancesOfClassMethod.invoke(null, new Object[]{c, assignable}); } public static long[] countInstancesofClasses(Class<?>[] classes, boolean assignable) throws Exception { return (long[]) countInstancesOfClassesMethod.invoke( null, new Object[]{classes, assignable}); } } }