/* * Created on 1 Nov 2006 * Created by Paul Gardner * Copyright (C) 2006 Aelitis, All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * AELITIS, SAS au capital de 63.529,40 euros * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France. * */ package com.aelitis.azureus.core.stats; import java.util.*; import java.util.regex.Pattern; import org.gudy.azureus2.core3.util.AEDiagnostics; import org.gudy.azureus2.core3.util.AEDiagnosticsEvidenceGenerator; import org.gudy.azureus2.core3.util.Debug; import org.gudy.azureus2.core3.util.IndentWriter; import org.gudy.azureus2.core3.util.Timer; import org.gudy.azureus2.core3.util.TimerEvent; import org.gudy.azureus2.core3.util.TimerEventPerformer; import com.aelitis.azureus.core.util.CopyOnWriteList; import com.aelitis.azureus.core.util.average.Average; import com.aelitis.azureus.core.util.average.AverageFactory; public class AzureusCoreStats { public static final int AVERAGE_PERIOD = 1000; public static final String ST_ALL = ".*"; // DISK public static final String ST_DISK = "disk.*"; public static final String ST_DISK_READ_QUEUE_LENGTH = "disk.read.queue.length"; public static final String ST_DISK_READ_QUEUE_BYTES = "disk.read.queue.bytes"; public static final String ST_DISK_READ_REQUEST_COUNT = "disk.read.request.count"; public static final String ST_DISK_READ_REQUEST_SINGLE = "disk.read.request.single"; public static final String ST_DISK_READ_REQUEST_MULTIPLE = "disk.read.request.multiple"; public static final String ST_DISK_READ_REQUEST_BLOCKS = "disk.read.request.blocks"; public static final String ST_DISK_READ_BYTES_TOTAL = "disk.read.bytes.total"; public static final String ST_DISK_READ_BYTES_SINGLE = "disk.read.bytes.single"; public static final String ST_DISK_READ_BYTES_MULTIPLE = "disk.read.bytes.multiple"; public static final String ST_DISK_READ_IO_TIME = "disk.read.io.time"; public static final String ST_DISK_READ_IO_COUNT = "disk.read.io.count"; public static final String ST_DISK_WRITE_QUEUE_LENGTH = "disk.write.queue.length"; public static final String ST_DISK_WRITE_QUEUE_BYTES = "disk.write.queue.bytes"; public static final String ST_DISK_WRITE_REQUEST_COUNT = "disk.write.request.count"; public static final String ST_DISK_WRITE_REQUEST_BLOCKS = "disk.write.request.blocks"; public static final String ST_DISK_WRITE_BYTES_TOTAL = "disk.write.bytes.total"; public static final String ST_DISK_WRITE_BYTES_SINGLE = "disk.write.bytes.single"; public static final String ST_DISK_WRITE_BYTES_MULTIPLE = "disk.write.bytes.multiple"; public static final String ST_DISK_WRITE_IO_TIME = "disk.write.io.time"; public static final String ST_DISK_WRITE_IO_COUNT = "disk.write.io.count"; // NETWORK public static final String ST_NET_WRITE_CONTROL_WAIT_COUNT = "net.write.control.wait.count"; public static final String ST_NET_WRITE_CONTROL_NP_COUNT = "net.write.control.np.count"; public static final String ST_NET_WRITE_CONTROL_P_COUNT = "net.write.control.p.count"; public static final String ST_NET_WRITE_CONTROL_ENTITY_COUNT = "net.write.control.entity.count"; public static final String ST_NET_WRITE_CONTROL_CON_COUNT = "net.write.control.con.count"; public static final String ST_NET_WRITE_CONTROL_READY_CON_COUNT = "net.write.control.ready.con.count"; public static final String ST_NET_WRITE_CONTROL_READY_BYTE_COUNT = "net.write.control.ready.byte.count"; public static final String ST_NET_READ_CONTROL_LOOP_COUNT = "net.read.control.loop.count"; public static final String ST_NET_READ_CONTROL_NP_COUNT = "net.read.control.np.count"; public static final String ST_NET_READ_CONTROL_P_COUNT = "net.read.control.p.count"; public static final String ST_NET_READ_CONTROL_WAIT_COUNT = "net.read.control.wait.count"; public static final String ST_NET_READ_CONTROL_ENTITY_COUNT = "net.read.control.entity.count"; public static final String ST_NET_READ_CONTROL_CON_COUNT = "net.read.control.con.count"; public static final String ST_NET_READ_CONTROL_READY_CON_COUNT = "net.read.control.ready.con.count"; // TCP public static final String ST_NET_TCP_OUT_CONNECT_QUEUE_LENGTH = "net.tcp.outbound.connect.queue.length"; public static final String ST_NET_TCP_OUT_PENDING_QUEUE_LENGTH = "net.tcp.outbound.pending.queue.length"; public static final String ST_NET_TCP_OUT_CANCEL_QUEUE_LENGTH = "net.tcp.outbound.cancel.queue.length"; public static final String ST_NET_TCP_OUT_CLOSE_QUEUE_LENGTH = "net.tcp.outbound.close.queue.length"; public static final String ST_NET_TCP_SELECT_WRITE_COUNT = "net.tcp.select.write.count"; public static final String ST_NET_TCP_SELECT_READ_COUNT = "net.tcp.select.read.count"; // HTTP public static final String ST_NET_HTTP_IN_REQUEST_COUNT = "net.http.inbound.request.count"; public static final String ST_NET_HTTP_IN_REQUEST_OK_COUNT = "net.http.inbound.request.ok.count"; public static final String ST_NET_HTTP_IN_REQUEST_INVALID_COUNT = "net.http.inbound.request.invalid.count"; public static final String ST_NET_HTTP_IN_REQUEST_WEBSEED_COUNT = "net.http.inbound.request.webseed.count"; public static final String ST_NET_HTTP_IN_REQUEST_GETRIGHT_COUNT = "net.http.inbound.request.getright.count"; // Peer Control public static final String ST_PEER_CONTROL_SCHEDULE_COUNT = "peer.control.schedule.count"; public static final String ST_PEER_CONTROL_LOOP_COUNT = "peer.control.loop.count"; public static final String ST_PEER_CONTROL_YIELD_COUNT = "peer.control.yield.count"; public static final String ST_PEER_CONTROL_WAIT_COUNT = "peer.control.wait.count"; public static final String ST_PEER_CONTROL_WAIT_TIME = "peer.control.wait.time"; // Peer Manager public static final String ST_PEER_MANAGER_COUNT = "peer.manager.count"; public static final String ST_PEER_MANAGER_PEER_COUNT = "peer.manager.peer.count"; public static final String ST_PEER_MANAGER_PEER_SNUBBED_COUNT = "peer.manager.peer.snubbed.count"; public static final String ST_PEER_MANAGER_PEER_STALLED_DISK_COUNT = "peer.manager.peer.stalled.disk.count"; // Tracker public static final String ST_TRACKER_READ_BYTES = "tracker.read.bytes.total"; public static final String ST_TRACKER_WRITE_BYTES = "tracker.write.bytes.total"; public static final String ST_TRACKER_ANNOUNCE_COUNT = "tracker.announce.count"; public static final String ST_TRACKER_ANNOUNCE_TIME = "tracker.announce.time"; public static final String ST_TRACKER_SCRAPE_COUNT = "tracker.scrape.count"; public static final String ST_TRACKER_SCRAPE_TIME = "tracker.scrape.time"; // xfer (persistent) public static final String ST_XFER_UPLOADED_PROTOCOL_BYTES = "xfer.upload.protocol.bytes.total"; public static final String ST_XFER_UPLOADED_DATA_BYTES = "xfer.upload.data.bytes.total"; public static final String ST_XFER_DOWNLOADED_PROTOCOL_BYTES = "xfer.download.protocol.bytes.total"; public static final String ST_XFER_DOWNLOADED_DATA_BYTES = "xfer.download.data.bytes.total"; public static final String POINT = "Point"; public static final String CUMULATIVE = "Cumulative"; private static final List stats_names = new ArrayList(); private static final Map stats_types = new HashMap(); private static final String[][] _ST_ALL = { { ST_DISK_READ_QUEUE_LENGTH, POINT }, { ST_DISK_READ_QUEUE_BYTES, POINT }, { ST_DISK_READ_REQUEST_COUNT, CUMULATIVE }, { ST_DISK_READ_REQUEST_SINGLE, CUMULATIVE }, { ST_DISK_READ_REQUEST_MULTIPLE, CUMULATIVE }, { ST_DISK_READ_REQUEST_BLOCKS, CUMULATIVE }, { ST_DISK_READ_BYTES_TOTAL, CUMULATIVE }, { ST_DISK_READ_BYTES_SINGLE, CUMULATIVE }, { ST_DISK_READ_BYTES_MULTIPLE, CUMULATIVE }, { ST_DISK_READ_IO_TIME, CUMULATIVE }, { ST_DISK_READ_IO_COUNT, CUMULATIVE }, { ST_DISK_WRITE_QUEUE_LENGTH, POINT }, { ST_DISK_WRITE_QUEUE_BYTES, POINT }, { ST_DISK_WRITE_REQUEST_COUNT, CUMULATIVE }, { ST_DISK_WRITE_REQUEST_BLOCKS, CUMULATIVE }, { ST_DISK_WRITE_BYTES_TOTAL, CUMULATIVE }, { ST_DISK_WRITE_BYTES_SINGLE, CUMULATIVE }, { ST_DISK_WRITE_BYTES_MULTIPLE, CUMULATIVE }, { ST_DISK_WRITE_IO_TIME, CUMULATIVE }, { ST_DISK_WRITE_IO_COUNT, CUMULATIVE }, { ST_NET_WRITE_CONTROL_WAIT_COUNT, CUMULATIVE }, { ST_NET_WRITE_CONTROL_P_COUNT, CUMULATIVE }, { ST_NET_WRITE_CONTROL_NP_COUNT, CUMULATIVE }, { ST_NET_WRITE_CONTROL_ENTITY_COUNT, POINT }, { ST_NET_WRITE_CONTROL_CON_COUNT, POINT }, { ST_NET_WRITE_CONTROL_READY_CON_COUNT, POINT }, { ST_NET_WRITE_CONTROL_READY_BYTE_COUNT, POINT }, { ST_NET_READ_CONTROL_LOOP_COUNT, CUMULATIVE }, { ST_NET_READ_CONTROL_P_COUNT, CUMULATIVE }, { ST_NET_READ_CONTROL_NP_COUNT, CUMULATIVE }, { ST_NET_READ_CONTROL_WAIT_COUNT, CUMULATIVE }, { ST_NET_READ_CONTROL_ENTITY_COUNT, POINT }, { ST_NET_READ_CONTROL_CON_COUNT, POINT }, { ST_NET_READ_CONTROL_READY_CON_COUNT, POINT }, { ST_NET_TCP_OUT_CONNECT_QUEUE_LENGTH, POINT }, { ST_NET_TCP_OUT_PENDING_QUEUE_LENGTH, POINT }, { ST_NET_TCP_OUT_CANCEL_QUEUE_LENGTH, POINT }, { ST_NET_TCP_OUT_CLOSE_QUEUE_LENGTH, POINT }, { ST_NET_TCP_SELECT_WRITE_COUNT, CUMULATIVE }, { ST_NET_TCP_SELECT_READ_COUNT, CUMULATIVE }, { ST_NET_HTTP_IN_REQUEST_COUNT, CUMULATIVE }, { ST_NET_HTTP_IN_REQUEST_OK_COUNT, CUMULATIVE }, { ST_NET_HTTP_IN_REQUEST_INVALID_COUNT, CUMULATIVE }, { ST_NET_HTTP_IN_REQUEST_WEBSEED_COUNT, CUMULATIVE }, { ST_NET_HTTP_IN_REQUEST_GETRIGHT_COUNT, CUMULATIVE }, { ST_PEER_CONTROL_SCHEDULE_COUNT, CUMULATIVE }, { ST_PEER_CONTROL_LOOP_COUNT, CUMULATIVE }, { ST_PEER_CONTROL_YIELD_COUNT, CUMULATIVE }, { ST_PEER_CONTROL_WAIT_COUNT, CUMULATIVE }, { ST_PEER_CONTROL_WAIT_TIME, CUMULATIVE }, { ST_PEER_MANAGER_COUNT, POINT }, { ST_PEER_MANAGER_PEER_COUNT, POINT }, { ST_PEER_MANAGER_PEER_SNUBBED_COUNT, POINT }, { ST_PEER_MANAGER_PEER_STALLED_DISK_COUNT, POINT }, { ST_TRACKER_READ_BYTES, CUMULATIVE }, { ST_TRACKER_WRITE_BYTES, CUMULATIVE }, { ST_TRACKER_ANNOUNCE_COUNT, CUMULATIVE }, { ST_TRACKER_ANNOUNCE_TIME, CUMULATIVE }, { ST_TRACKER_SCRAPE_COUNT, CUMULATIVE }, { ST_TRACKER_SCRAPE_TIME, CUMULATIVE }, { ST_XFER_UPLOADED_PROTOCOL_BYTES, CUMULATIVE }, { ST_XFER_UPLOADED_DATA_BYTES, CUMULATIVE }, { ST_XFER_DOWNLOADED_PROTOCOL_BYTES, CUMULATIVE }, { ST_XFER_DOWNLOADED_DATA_BYTES, CUMULATIVE }, }; static{ addStatsDefinitions( _ST_ALL ); AEDiagnostics.addEvidenceGenerator( new AEDiagnosticsEvidenceGenerator() { public void generate( IndentWriter writer ) { writer.println( "Stats" ); boolean turn_on_averages = !getEnableAverages(); try{ writer.indent(); if ( turn_on_averages ){ setEnableAverages( true ); try{ Thread.sleep( AVERAGE_PERIOD * 5 ); }catch( Throwable e ){ } } Set types = new HashSet(); types.add( ST_ALL ); Map reply = getStats( types ); Iterator it = reply.entrySet().iterator(); List lines = new ArrayList(); while( it.hasNext()){ Map.Entry entry = (Map.Entry)it.next(); lines.add( entry.getKey() + " -> " + entry.getValue()); } Collections.sort( lines ); for ( int i=0;i<lines.size();i++){ writer.println((String)lines.get(i)); } }finally{ if ( turn_on_averages ){ setEnableAverages( false ); } writer.exdent(); } } }); } private static final CopyOnWriteList providers = new CopyOnWriteList(); private static Map averages = new HashMap(); private static boolean enable_averages; private static Timer average_timer; private static CopyOnWriteList provider_listeners = new CopyOnWriteList(); private static CopyOnWriteList derived_generators = new CopyOnWriteList(); public static void addStatsDefinitions( String[][] stats ) { for (int i=0;i<stats.length;i++){ String name = stats[i][0]; stats_names.add( name ); stats_types.put( name, stats[i][1] ); } } public static Map getStats( Set types ) { Set expanded = new HashSet(); Iterator it = types.iterator(); while( it.hasNext()){ String type = (String)it.next(); if ( type.endsWith( ".average" )){ type = type.substring(0,type.length()-8); } if ( !type.endsWith("*")){ type = type + ".*"; } Pattern pattern = Pattern.compile( type ); for (int i=0;i<stats_names.size();i++){ String s = (String)stats_names.get(i); if ( pattern.matcher( s ).matches()){ expanded.add( s ); } } Iterator provider_it = providers.iterator(); while( provider_it.hasNext()){ Object[] provider_entry = (Object[])provider_it.next(); Set provider_types = (Set)provider_entry[0]; Iterator pt_it = provider_types.iterator(); while( pt_it.hasNext()){ String s = (String)pt_it.next(); if ( pattern.matcher( s ).matches()){ expanded.add( s ); } } } Iterator derived_it = derived_generators.iterator(); while( derived_it.hasNext()){ try{ ((derivedStatsGenerator)derived_it.next()).match( pattern, expanded ); }catch( Throwable e ){ Debug.printStackTrace( e ); } } } Map result = getStatsSupport( expanded ); Map ave = averages; if ( ave != null ){ it = result.keySet().iterator(); Map ave_results = new HashMap(); while( it.hasNext()){ String key = (String)it.next(); Object[] a_entry = (Object[])ave.get( key ); if ( a_entry != null ){ Average average = (Average)a_entry[0]; ave_results.put( key + ".average", new Long((long)average.getAverage())); } } result.putAll( ave_results ); } Iterator derived_it = derived_generators.iterator(); while( derived_it.hasNext()){ try{ ((derivedStatsGenerator)derived_it.next()).generate( result ); }catch( Throwable e ){ Debug.printStackTrace( e ); } } return( result ); } protected static Map getStatsSupport( Set types ) { Map result = new HashMap(); Iterator it = providers.iterator(); while( it.hasNext()){ Object[] provider_entry = (Object[])it.next(); Map provider_result = new HashMap(); Set target_types; if ( types == null ){ target_types = (Set)provider_entry[0]; }else{ target_types = types; } try{ ((AzureusCoreStatsProvider)provider_entry[1]).updateStats( target_types, provider_result ); Iterator pit = provider_result.entrySet().iterator(); while( pit.hasNext()){ Map.Entry pe = (Map.Entry)pit.next(); String key = (String)pe.getKey(); Object obj = pe.getValue(); if ( obj instanceof Long ){ Long old = (Long)result.get(key); if ( old == null ){ result.put( key, obj ); }else{ long v = ((Long)obj).longValue(); result.put( key, new Long( v + old.longValue())); } }else{ result.put( key, obj ); } } }catch( Throwable e ){ Debug.printStackTrace(e); } } return( result ); } public static void registerProvider( Set types, AzureusCoreStatsProvider provider ) { synchronized( providers ){ providers.add( new Object[]{ types, provider }); } fireProvidersChangeListeners(); } public static void addProvidersChangeListener( providersChangeListener l ) { provider_listeners.add( l ); } protected static void fireProvidersChangeListeners() { Iterator it = provider_listeners.iterator(); while( it.hasNext()){ try{ ((providersChangeListener)it.next()).providersChanged(); }catch( Throwable e ){ Debug.printStackTrace(e); } } } public static void registerDerivedStatsGenerator( derivedStatsGenerator gen ) { derived_generators.add( gen ); } public static synchronized void setEnableAverages( boolean enabled ) { if ( enabled == enable_averages ){ return; } enable_averages = enabled; if ( enabled ){ if ( average_timer == null ){ average_timer = new Timer( "AzureusCoreStats:average" ); averages = new HashMap(); average_timer.addPeriodicEvent( AVERAGE_PERIOD, new TimerEventPerformer() { private Map ave = averages; public void perform( TimerEvent event ) { Map stats = getStatsSupport( null ); Iterator it = stats.entrySet().iterator(); boolean new_averages = false; while( it.hasNext()){ Map.Entry entry = (Map.Entry)it.next(); String key = (String)entry.getKey(); Object value = entry.getValue(); if ( value instanceof Long ){ long last_value; Average a; boolean new_average; Object[] a_entry = (Object[])ave.get( key ); if ( a_entry == null ){ a = AverageFactory.MovingImmediateAverage( 10 ); last_value = 0; a_entry = new Object[]{ a, value }; ave.put( key, a_entry ); new_averages = new_average = true; }else{ a = (Average)a_entry[0]; last_value = ((Long)a_entry[1]).longValue(); new_average = false; } if ( stats_types.get( key ) == CUMULATIVE ){ // skip initial value as 'last_value' is invalid if ( !new_average ){ a.update(((Long)value).longValue() - last_value); } }else{ a.update(((Long)value).longValue()); } a_entry[1] = value; } } if ( new_averages ){ fireProvidersChangeListeners(); } } }); } }else{ if ( average_timer != null ){ average_timer.destroy(); average_timer = null; averages = null; } } } public static synchronized boolean getEnableAverages() { return( enable_averages ); } public interface providersChangeListener { public void providersChanged(); } public interface derivedStatsGenerator { public void match( Pattern p, Set required ); public void generate( Map map ); } }