001 /*******************************************************************************
002 * Copyright (c) 2009 Progress Software, Inc.
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 }