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 *******************************************************************************/
010package org.fusesource.hawtjni.runtime;
011
012import java.io.PrintStream;
013import java.util.ArrayList;
014import java.util.Arrays;
015import java.util.Collection;
016import java.util.Collections;
017import java.util.HashMap;
018import 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 *      <pre>
028 *      StatsInterface si = MyFooStatsInterface.INSTANCE;
029 *      NativeStats stats = new NativeStats(si); 
030 *      ... // your code
031 *      stats.diff().dump(System.out);
032 *      </pre>
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 *      <pre>
037 *      stats.snapshot().dump(System.out);
038 *      </pre>
039 * </li>
040 * </ol>
041 * 
042 * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
043 */
044public 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}