001    /*******************************************************************************
002     * Copyright (C) 2009-2011 FuseSource Corp.
003     * Copyright (c) 2004, 2006 IBM Corporation and others.
004     * 
005     * All rights reserved. This program and the accompanying materials
006     * are made available under the terms of the Eclipse Public License v1.0
007     * which accompanies this distribution, and is available at
008     * http://www.eclipse.org/legal/epl-v10.html
009     *******************************************************************************/
010    package org.fusesource.hawtjni.runtime;
011    
012    import java.io.PrintStream;
013    import java.util.ArrayList;
014    import java.util.Arrays;
015    import java.util.Collection;
016    import java.util.Collections;
017    import java.util.HashMap;
018    import java.util.Map.Entry;
019    
020    /**
021     * Instructions on how to use the NativeStats tool with a standalone SWT
022     * example:
023     * <ol>
024     * <li> Compile the native libraries defining the NATIVE_STATS flag.</li>
025     * <li> Add the following code around the sections of
026     *      interest to dump the native calls done in that section. 
027     *      <code><pre>
028     *      StatsInterface si = MyFooStatsInterface.INSTANCE;
029     *      NativeStats stats = new NativeStats(si); 
030     *      ... // your code
031     *      stats.diff().dump(System.out);
032     *      </pre></code>
033     * </li>
034     * <li> Or add the following code at a given point to dump a snapshot of
035     *      the native calls done until that point.
036     *      <code><pre>
037     *      stats.snapshot().dump(System.out);
038     *      </pre></code>
039     * </li>
040     * </ol>
041     * 
042     * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
043     */
044    public class NativeStats {
045    
046        public interface StatsInterface {
047            String getNativeClass();
048            int functionCount();
049            String functionName(int ordinal);
050            int functionCounter(int ordinal);
051        }
052        
053        public static class NativeFunction implements Comparable<NativeFunction> {
054            private final int ordinal;
055            private final String name;
056            private int counter;
057    
058            public NativeFunction(int ordinal, String name, int callCount) {
059                this.ordinal = ordinal;
060                this.name = name;
061                this.counter = callCount;
062            }
063            void subtract(NativeFunction func) {
064                this.counter -= func.counter;
065            }
066            
067            public int getCounter() {
068                return counter;
069            }
070            public void setCounter(int counter) {
071                this.counter = counter;
072            }
073            
074            public String getName() {
075                return name;
076            }
077    
078            public int getOrdinal() {
079                return ordinal;
080            }
081            
082            public int compareTo(NativeFunction func) {
083                return func.counter - counter;
084            }
085            
086            public void reset() {
087                counter=0;
088            }
089            
090            public NativeFunction copy() {
091                return new NativeFunction(ordinal, name, counter);
092            }
093        }
094    
095        private final HashMap<StatsInterface, ArrayList<NativeFunction>> snapshot;
096        
097        public NativeStats(StatsInterface... classes) {
098            this(Arrays.asList(classes)); 
099        }
100    
101        public NativeStats(Collection<StatsInterface> classes) {
102            this(snapshot(classes)); 
103        }
104        
105        private NativeStats(HashMap<StatsInterface, ArrayList<NativeFunction>> snapshot) {
106            this.snapshot = snapshot;
107        }
108    
109        public void reset() {
110            for (ArrayList<NativeFunction> functions : snapshot.values()) {
111                for (NativeFunction function : functions) {
112                    function.reset();
113                }
114            }
115        }
116        
117        public void update() {
118            for (Entry<StatsInterface, ArrayList<NativeFunction>> entry : snapshot.entrySet()) {
119                StatsInterface si = entry.getKey();
120                for (NativeFunction function : entry.getValue()) {
121                    function.setCounter( si.functionCounter(function.getOrdinal()) );
122                }
123            }
124        }
125        
126        public NativeStats snapshot() {
127            NativeStats copy = copy();
128            copy.update();
129            return copy;
130        }
131    
132        public NativeStats copy() {
133            HashMap<StatsInterface, ArrayList<NativeFunction>> rc = new HashMap<StatsInterface, ArrayList<NativeFunction>>(snapshot.size()*2);
134            for (Entry<StatsInterface, ArrayList<NativeFunction>> entry : snapshot.entrySet()) {
135                ArrayList<NativeFunction> list = new ArrayList<NativeFunction>(entry.getValue().size());
136                for (NativeFunction function : entry.getValue()) {
137                    list.add(function.copy());
138                }
139                rc.put(entry.getKey(), list);
140            }
141            return new NativeStats(rc);
142        }
143        
144        public NativeStats diff() {
145            HashMap<StatsInterface, ArrayList<NativeFunction>> rc = new HashMap<StatsInterface, ArrayList<NativeFunction>>(snapshot.size()*2);
146            for (Entry<StatsInterface, ArrayList<NativeFunction>> entry : snapshot.entrySet()) {
147                StatsInterface si = entry.getKey();
148                ArrayList<NativeFunction> list = new ArrayList<NativeFunction>(entry.getValue().size());
149                for (NativeFunction original : entry.getValue()) {
150                    NativeFunction copy = original.copy();
151                    copy.setCounter( si.functionCounter(copy.getOrdinal()) );
152                    copy.subtract(original);
153                    list.add(copy);
154                }
155                rc.put(si, list);
156            }
157            return new NativeStats(rc);
158        }
159    
160        /**
161         * Dumps the stats to the print stream in a JSON format.
162         * @param ps
163         */
164        public void dump(PrintStream ps) {
165            boolean firstSI=true;
166            for (Entry<StatsInterface, ArrayList<NativeFunction>> entry : snapshot.entrySet()) {
167                StatsInterface si = entry.getKey();
168                ArrayList<NativeFunction> funcs = entry.getValue();
169    
170                int total = 0;
171                for (NativeFunction func : funcs) {
172                    total += func.getCounter();
173                }
174                
175                if( !firstSI ) {
176                    ps.print(", ");
177                }
178                firstSI=false;
179                ps.print("[");
180                if( total>0 ) {
181                    ps.println("{ ");
182                    ps.println("  \"class\": \""+si.getNativeClass()+"\",");
183                    ps.println("  \"total\": "+total+", ");
184                      ps.print("  \"functions\": {");
185                    boolean firstFunc=true;
186                    for (NativeFunction func : funcs) {
187                        if (func.getCounter() > 0) {
188                            if( !firstFunc ) {
189                                ps.print(",");
190                            }
191                            firstFunc=false;
192                            ps.println();
193                            ps.print("    \""+func.getName()+"\": "+func.getCounter());
194                        }
195                    }
196                    ps.println();
197                    ps.println("  }");
198                    ps.print("}");
199                }            
200                ps.print("]");
201            }
202        }
203    
204        static private HashMap<StatsInterface, ArrayList<NativeFunction>> snapshot(Collection<StatsInterface> classes) {
205             HashMap<StatsInterface, ArrayList<NativeFunction>> rc = new HashMap<StatsInterface, ArrayList<NativeFunction>>();
206            for (StatsInterface sc : classes) {
207                int count = sc.functionCount();
208                ArrayList<NativeFunction> functions = new ArrayList<NativeFunction>(count);
209                for (int i = 0; i < count; i++) {
210                    String name = (String) sc.functionName(i);
211                    functions.add(new NativeFunction(i, name, 0));
212                }
213                Collections.sort(functions);
214                rc.put(sc, functions);
215            }
216            return rc;
217        }
218        
219    
220    }