001/*
002 * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.hotspot.logging;
024
025import java.lang.reflect.*;
026import java.util.*;
027import java.util.concurrent.*;
028import java.util.concurrent.atomic.*;
029
030import com.oracle.graal.debug.*;
031
032/**
033 * A java.lang.reflect proxy that hierarchically logs all method invocations along with their
034 * parameters and return values.
035 */
036public class CountingProxy<T> implements InvocationHandler {
037
038    public static final boolean ENABLED = Boolean.valueOf(System.getProperty("jvmci.countcalls"));
039
040    private T delegate;
041
042    private ConcurrentHashMap<Method, AtomicLong> calls = new ConcurrentHashMap<>();
043
044    public CountingProxy(T delegate) {
045        assert ENABLED;
046        TTY.println("Counting proxy for " + delegate.getClass().getSimpleName() + " created");
047        this.delegate = delegate;
048        proxies.add(this);
049    }
050
051    @Override
052    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
053        int argCount = args == null ? 0 : args.length;
054        if (method.getParameterTypes().length != argCount) {
055            throw new RuntimeException("wrong parameter count");
056        }
057        final Object result;
058        if (!calls.containsKey(method)) {
059            calls.putIfAbsent(method, new AtomicLong(0));
060        }
061        AtomicLong count = calls.get(method);
062        count.incrementAndGet();
063        try {
064            if (args == null) {
065                result = method.invoke(delegate);
066            } else {
067                result = method.invoke(delegate, args);
068            }
069        } catch (InvocationTargetException e) {
070            throw e.getCause();
071        }
072        return result;
073    }
074
075    public static <T> T getProxy(Class<T> interf, T delegate) {
076        Class<?>[] interfaces = ProxyUtil.getAllInterfaces(delegate.getClass());
077        Object obj = Proxy.newProxyInstance(interf.getClassLoader(), interfaces, new CountingProxy<>(delegate));
078        return interf.cast(obj);
079    }
080
081    private static ArrayList<CountingProxy<?>> proxies = new ArrayList<>();
082
083    static {
084        if (ENABLED) {
085            Runtime.getRuntime().addShutdownHook(new Thread() {
086
087                @Override
088                public void run() {
089                    for (CountingProxy<?> proxy : proxies) {
090                        proxy.print();
091                    }
092                }
093            });
094        }
095    }
096
097    protected void print() {
098        long sum = 0;
099        for (Map.Entry<Method, AtomicLong> entry : calls.entrySet()) {
100            Method method = entry.getKey();
101            long count = entry.getValue().get();
102            sum += count;
103            TTY.println(delegate.getClass().getSimpleName() + "." + method.getName() + ": " + count);
104        }
105        TTY.println(delegate.getClass().getSimpleName() + " calls: " + sum);
106    }
107}