001/*
002 * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.nuxeo.runtime.javaagent;
017
018import java.lang.instrument.Instrumentation;
019import java.lang.reflect.Array;
020import java.lang.reflect.Field;
021import java.lang.reflect.Modifier;
022import java.security.AccessController;
023import java.security.PrivilegedAction;
024import java.util.IdentityHashMap;
025import java.util.Map;
026
027public class NuxeoAgent {
028
029    protected static NuxeoAgent agent = new NuxeoAgent();
030
031    protected Instrumentation instrumentation;
032
033    public static void premain(String args, Instrumentation inst) {
034        agent.instrumentation = inst;
035    }
036
037    public static void agentmain(String args, Instrumentation inst) {
038        agent.instrumentation = inst;
039    }
040
041    public long sizeOf(final Object o) {
042        return instrumentation.getObjectSize(o);
043    }
044
045    public long deepSizeOf(final Object o) {
046        if (o == null) {
047            throw new NullPointerException();
048        }
049
050        return AccessController.doPrivileged(new PrivilegedAction<Long>() {
051            @Override
052            public Long run() {
053                return new GraphSizeProfiler().visit(o);
054            }
055        });
056    }
057
058    protected class GraphSizeProfiler {
059
060        protected final Map<Object, Object> visited = new IdentityHashMap<Object, Object>();
061
062        protected long visit(Object each) {
063            if (each == null) {
064                return 0;
065            }
066            if (visited.containsKey(each)) {
067                return 0;
068            }
069            visited.put(each, each);
070            long size = instrumentation.getObjectSize(each);
071            Class<?> eachType = each.getClass();
072            if (eachType.isArray()) {
073                if (eachType.getComponentType().isPrimitive()) {
074                    return 0;
075                }
076                for (int i = 0; i < Array.getLength(each); i++) {
077                    size += visit(Array.get(each, i));
078                }
079            } else {
080                size += visit(each, eachType);
081            }
082            return size;
083        }
084
085        protected long visit(Object each, Class<?> eachType) {
086            if (eachType.equals(Object.class)) {
087                return 0;
088            }
089            long size = 0;
090            for (Field eachField : eachType.getDeclaredFields()) {
091                size += visit(each, eachField);
092            }
093            return size + visit(each, eachType.getSuperclass());
094        }
095
096        protected long visit(Object each, Field eachField) {
097            if ((eachField.getModifiers() & Modifier.STATIC) != 0) {
098                return 0;
099            }
100            if (eachField.getType().isPrimitive()) {
101                return 0;
102            }
103            boolean oldAccessible = eachField.isAccessible();
104            eachField.setAccessible(true);
105            try {
106                return visit(eachField.get(each));
107            } catch (Exception e) {
108                throw new RuntimeException("Exception trying to access field " + eachField, e);
109            } finally {
110                eachField.setAccessible(oldAccessible);
111            }
112        }
113    }
114
115}