001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.modules.turbo;
042:
043: import java.io.*;
044: import java.util.Date;
045: import java.util.Set;
046: import java.util.HashSet;
047: import java.util.Iterator;
048:
049: /**
050: * Statistics support for {@link Turbo}.
051: *
052: * <p>Results analysis allows to determine if cache
053: * have any effect.
054: *
055: * @author Petr Kuzel
056: */
057: class Statistics {
058:
059: private static final Statistics NOP_INSTANCE = new NOP();
060: private static final int REMOVED_BATCH_INTERVAL = 1000 * 10; // 10s
061:
062: private long requests = 0;
063: private long memoryHits = 0;
064: private long diskHits = 0;
065:
066: // background loading
067: private long threads = 0;
068: private long duplicates = 0;
069: private long maxQueueSize = 0;
070:
071: // Memory.liveentitiesMap utilization
072: private long maxMemoryEntries = 0;
073: private long gcCounter = 0;
074: private long newCounter = 0;
075: private int removedInvocaionCounter = 0;
076: private long removedInvocationTime = System.currentTimeMillis()
077: - REMOVED_BATCH_INTERVAL;
078: /** Holds keys string reprentation to avoid memory leaks. */
079: private Set recentKeys;
080:
081: // cache instance identification fields
082: private static volatile int idPool = 1;
083: private final int id;
084: private final Exception origin;
085:
086: private PrintWriter out;
087:
088: /** Creates new statistics instance according to
089: * <tt>netbeans.experimental.vcsTurboStatistics</tt> system
090: * property:
091: * <ul>
092: * <li><tt>none</tt> (default) no-op implementaion
093: * <li><tt>mini</tt> logs fast events
094: * <li><tt>performance</tt> logs also heavy events slowing down Turbo and increasing it's memory requirements.
095: * </ul>
096: */
097: public static Statistics createInstance() {
098: if ("none".equalsIgnoreCase(System.getProperty(
099: "netbeans.experimental.vcsTurboStatistics", "none"))) { // NOI18N
100: return NOP_INSTANCE;
101: } else {
102: return new Statistics();
103: }
104: }
105:
106: private Statistics() {
107: origin = new RuntimeException();
108: id = idPool++;
109: }
110:
111: /**
112: * Checks if additional logging required for detailed performance evaluation is required.
113: */
114: private static boolean logPerformance() {
115: return System.getProperty(
116: "netbeans.experimental.vcsTurboStatistics", "mini")
117: .equalsIgnoreCase("performance"); // NOI18N
118: }
119:
120: /** Key created adding permision to store it in memory layer. */
121: public void keyAdded(Object key) {
122: if (key != null)
123: println("EK+ " + key); // NOi18N
124: newCounter++;
125: long allocated = newCounter - gcCounter;
126: if (allocated > maxMemoryEntries) {
127: maxMemoryEntries = allocated;
128: }
129: if (recentKeys != null) {
130: assert recentKeys.add(key.toString()) : "Key added for the second time: "
131: + key;
132: }
133: }
134:
135: /** Key was removed from memory. */
136: public void keyRemoved(Object key) {
137: if (key != null)
138: println("EK- " + key); // NOi18N
139: gcCounter++;
140: if (recentKeys != null) {
141: recentKeys.remove(key.toString());
142: }
143: }
144:
145: /**
146: * Detect reclaimed keys. It lods first results on the second call.
147: *
148: * <p>It's heavy event.
149: */
150: public void computeRemoved(Set keys) {
151: if (logPerformance() == false)
152: return;
153:
154: if (System.currentTimeMillis() - removedInvocationTime > REMOVED_BATCH_INTERVAL)
155: return;
156:
157: removedInvocaionCounter++;
158: Iterator it = keys.iterator();
159: Set currentKeys = new HashSet(keys.size());
160: while (it.hasNext()) {
161: String stringKey = it.next().toString();
162: currentKeys.add(stringKey);
163: }
164:
165: if (recentKeys != null) {
166: recentKeys.removeAll(currentKeys);
167: int reclaimed = recentKeys.size();
168: gcCounter += reclaimed;
169:
170: if (reclaimed > 0) {
171: println("MSG [" + new Date().toString()
172: + "] reclaimed keys:"); // NOI18N
173: Iterator itr = recentKeys.iterator();
174: while (itr.hasNext()) {
175: String next = itr.next().toString();
176: println("EK- " + next); // NOI18N
177: }
178: println("MSG EOL reclaimed keys"); // NOI18N
179: }
180: }
181:
182: removedInvocationTime = System.currentTimeMillis();
183: recentKeys = currentKeys;
184: }
185:
186: /** Turbo request arrived */
187: public void attributeRequest() {
188: requests++;
189: if (requests % 1000 == 0) {
190: printCacheStatistics();
191: }
192: }
193:
194: /** The client request was resolved by memory layer */
195: public void memoryHit() {
196: memoryHits++;
197: }
198:
199: /** new background thread spawned */
200: public void backgroundThread() {
201: threads++;
202: }
203:
204: /** Duplicate request eliminated from queue. */
205: public void duplicate() {
206: duplicates++;
207: }
208:
209: public void queueSize(int size) {
210: if (size > maxQueueSize) {
211: maxQueueSize = size;
212: }
213: }
214:
215: /** The client request was resolved at providers layer */
216: public void providerHit() {
217: diskHits++;
218: }
219:
220: public void shutdown() {
221: printCacheStatistics();
222: out.flush();
223: out.close();
224: // System.out.println(" Statistics goes to " + Statistics.logPath()); // NOI18N
225: }
226:
227: private String logPath() {
228: return System.getProperty("java.io.tmpdir") + File.separator
229: + "netbeans-versioning-turbo-" + id + ".log"; // NOI18N
230: }
231:
232: private void printCacheStatistics() {
233: println("CS turbo.requests=" + requests); // NOI18N
234: println("CS memory.hits=" + memoryHits + " "
235: + (((float) memoryHits / (float) requests) * 100) + "%"); // NOI18N
236: println("CS provider.hits=" + diskHits + " "
237: + (((float) diskHits / (float) requests) * 100) + "%"); // NOI18N
238: if (removedInvocaionCounter >= 2) {
239: println("CS memory.max=" + maxMemoryEntries); // NOI18N
240: println("CS memory.entries=" + (newCounter - gcCounter)); // NOI18N
241: println("CS memory.entiresReclaimingRatio="
242: + (((float) gcCounter / (float) newCounter) * 100)
243: + "%"); // NOI18N
244: } else {
245: println("MSG No memory utilization data known, use -J-Dnetbeans.experimental.vcsTurboStatistics=performance.");
246: }
247: println("CS queue.threads=" + threads + " queue.duplicates="
248: + duplicates + " queue.maxSize=" + maxQueueSize); // NOI18N
249: println("MSG --"); // NOI18N
250: println("MSG turbo.log.Statistics on " + new Date().toString()); // NOI18N
251: }
252:
253: private synchronized void println(String s) {
254: if (out == null) {
255: String filePath = logPath();
256: try {
257: out = new PrintWriter(new BufferedWriter(
258: new FileWriter(filePath), 512));
259: } catch (IOException e) {
260: out = new PrintWriter(
261: new OutputStreamWriter(System.out), true);
262: }
263: out
264: .println("MSG EK followed by +/- denotes new memory cache entry/releasing it"); // NOI18N
265: out
266: .println("MSG CS describes summary statistics of memory and disk caches"); // NOI18N
267: out
268: .println("MSG the MSG prefix denotes free form messages"); // NOI18N
269: out.println("MSG turbo.Statistics instance serving:\n"); // NOI18N
270: StackTraceElement elements[] = origin.getStackTrace();
271: for (int i = 0; i < elements.length; i++) {
272: StackTraceElement element = elements[i];
273: out.println("MSG " + element.toString()); // NOI18N
274: }
275: out.println();
276: }
277: out.println(s);
278: }
279:
280: /** Logs nothing. */
281: private static final class NOP extends Statistics {
282: public void keyAdded(Object key) {
283: }
284:
285: public void computeRemoved(Set keys) {
286: }
287:
288: public void attributeRequest() {
289: }
290:
291: public void memoryHit() {
292: }
293:
294: public void backgroundThread() {
295: }
296:
297: public void duplicate() {
298: }
299:
300: public void queueSize(int size) {
301: }
302:
303: public void providerHit() {
304: }
305:
306: public void shutdown() {
307: }
308:
309: }
310: }
|