package alma.acs.profiling.orb; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.PrintStream; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.logging.Logger; import org.apache.commons.math.stat.descriptive.DescriptiveStatistics; import alma.acs.algorithms.DataBinner; import alma.acs.algorithms.DataBinner.BinnedTimeValues; import alma.acs.algorithms.DataBinner.TimeValue; import alma.acs.logging.ClientLogManager; import alma.acs.util.IsoDateFormat; /** * Used for http://almasw.hq.eso.org/almasw/bin/view/JAO/ABMCDBRetrieval, COMP-6488, * and possibly other logs that must be analyzed. * @author hsommer */ public class CdbCallStatistics { private final Logger logger; private final File dataFile; private final List<ProfilerMessage> messages; private static final String delim = "\t"; // delimiter for output files private final DecimalFormat df2 = new DecimalFormat("0.00"); public CdbCallStatistics(String dataFileName) throws IOException { logger = ClientLogManager.getAcsLogManager().getLoggerForApplication(CdbCallStatistics.class.getSimpleName(), false); dataFile = new File(dataFileName); OrbProfilerParser parser = new OrbProfilerParser(logger); messages = parser.parse(dataFile); } /** * Work in progress, not used yet. */ public void getDaoCallsWithParameter() throws IOException { OrbProfilerStatistics stat = new OrbProfilerStatistics(messages, logger); List<TimeValue<Integer>> calls = stat.getFinishedRequests("get_DAO"); BufferedReader reader = new BufferedReader(new FileReader(dataFile)); String line = null; while ((line = reader.readLine()) != null) { int pos = line.indexOf("Returning XML record for:"); if (pos > 0) { String xmlRecordPath = line.substring(pos + "Returning XML record for: ".length()); System.out.println(xmlRecordPath); // TODO: deal with call and stack } } reader.close(); } public void getCallTimeByMethodGroup() throws IOException { OrbProfilerStatistics stat = new OrbProfilerStatistics(messages, logger); Map<String, List<TimeValue<Integer>>> callGroups = new LinkedHashMap<String, List<TimeValue<Integer>>>(); // Corba object methods (TODO: add others) callGroups.put("_is_a", stat.getFinishedRequests("_is_a")); // methods for CDB change listening List<TimeValue<Integer>> listenerCalls = new ArrayList<TimeValue<Integer>>(); listenerCalls.addAll(stat.getFinishedRequests("add_change_listener")); listenerCalls.addAll(stat.getFinishedRequests("remove_change_listener")); listenerCalls.addAll(stat.getFinishedRequests("listen_for_changes")); Collections.sort(listenerCalls); callGroups.put("listener methods", listenerCalls); // the hefty XML requests callGroups.put("get_DAO", stat.getFinishedRequests("get_DAO")); // calls to a DAO remote object List<TimeValue<Integer>> daoCalls = new ArrayList<TimeValue<Integer>>(); daoCalls.addAll(stat.getFinishedRequests("get_long")); daoCalls.addAll(stat.getFinishedRequests("get_double")); daoCalls.addAll(stat.getFinishedRequests("get_string")); daoCalls.addAll(stat.getFinishedRequests("get_field_data")); daoCalls.addAll(stat.getFinishedRequests("get_string_seq")); daoCalls.addAll(stat.getFinishedRequests("get_long_seq")); daoCalls.addAll(stat.getFinishedRequests("get_double_seq")); daoCalls.addAll(stat.getFinishedRequests("destroy")); Collections.sort(daoCalls); callGroups.put("daoCalls", daoCalls); // returns a DAO remote object callGroups.put("get_DAO_Servant ", stat.getFinishedRequests("get_DAO_Servant")); // listing of available nodes List<TimeValue<Integer>> listingCalls = new ArrayList<TimeValue<Integer>>(); listingCalls.addAll(stat.getFinishedRequests("list_nodes")); listingCalls.addAll(stat.getFinishedRequests("list_daos")); Collections.sort(listingCalls); callGroups.put("list methods", listingCalls); // todo: Also use WDAL, WDAO methods File outFile = new File(getFileNameBase() + "_callTimeByMethodGroup.txt"); PrintStream pr = new PrintStream(outFile); pr.println("rdbCDB operation" + delim + "# calls" + delim + "average [ms]" + delim + "max(100%) [ms]" + delim + "max(99%) [ms]" + delim + "stdev [ms]"); for (String groupName : callGroups.keySet()) { List<TimeValue<Integer>> calls = callGroups.get(groupName); // call duration statistics DescriptiveStatistics callTimeStats = new DescriptiveStatistics(); for (TimeValue<Integer> timeValue : calls) { callTimeStats.addValue(timeValue.value); } pr.println(groupName + delim + calls.size() + delim + df2.format(callTimeStats.getMean()) + delim + (int)callTimeStats.getMax() + delim + (int)callTimeStats.getPercentile(99) + delim + df2.format(callTimeStats.getStandardDeviation())); } pr.close(); logger.info("Wrote " + outFile.getAbsolutePath()); } /** * @param operationName Can be null, to use calls to all operations. */ public void getCallFrequency(String operationName) throws FileNotFoundException { OrbProfilerStatistics stat = new OrbProfilerStatistics(messages, logger); final int binIntervalMillis = 1000; List<TimeValue<Integer>> allCalls = stat.getFinishedRequests(operationName); Collections.sort(allCalls); DataBinner binner = new DataBinner(); List<BinnedTimeValues<Integer>> binnedAllCalls = binner.binTimedData(allCalls, binIntervalMillis); String outFileName = getFileNameBase() + "_callFrequency" + (operationName != null ? operationName : "") + ".txt"; File outFile = new File(outFileName); PrintStream pr = new PrintStream(outFile); pr.println("time" + delim + "#calls/s" + delim + "duration max" + delim + "duration average"); for (BinnedTimeValues<Integer> binnedTimeValues : binnedAllCalls) { List<TimeValue<Integer>> timeValuesPerBin = binnedTimeValues.binnedData; DescriptiveStatistics callTimeStats = new DescriptiveStatistics(); for (TimeValue<Integer> timeValue : timeValuesPerBin) { callTimeStats.addValue(timeValue.value); } long numCallsPerBin = callTimeStats.getN(); // or timeValuesPerBin.size(); pr.println(timeString(binnedTimeValues.timeMillis) + delim + numCallsPerBin + delim + (numCallsPerBin > 0 ? df2.format(callTimeStats.getMax()) : 0) + delim + (numCallsPerBin > 0 ? df2.format(callTimeStats.getMean()) : 0) ); } pr.close(); logger.info("Wrote " + outFile.getAbsolutePath()); } /** * Produces a list of timestamps and the number of concurrent calls at that time. */ public void getConcurrentCalls() throws FileNotFoundException { OrbProfilerStatistics stat = new OrbProfilerStatistics(messages, logger); List<TimeValue<Integer>> concCalls = stat.getConcurrentCalls(); File outFile = new File(getFileNameBase() + "_concurrentCallCount.txt"); PrintStream pr = new PrintStream(outFile); pr.println("time" + delim + "# concurrent calls"); for (TimeValue<Integer> timeValue : concCalls) { pr.println(timeString(timeValue.timeMillis) + delim + timeValue.value); } pr.close(); logger.info("Wrote " + outFile.getAbsolutePath()); } private String getFileNameBase() { String fn = dataFile.getAbsolutePath(); int dotIndex = fn.lastIndexOf('.'); if (dotIndex > 0 && dotIndex > fn.length() - 10) { fn = fn.substring(0, dotIndex); } return fn; } private String timeString(long timeMillis) { return IsoDateFormat.formatDate(new Date(timeMillis)); } /** * @param args */ public static void main(String[] args) { try { if (args.length != 1) { throw new IllegalArgumentException("Data file name must be specified!"); } CdbCallStatistics inst = new CdbCallStatistics(args[0]); // inst.getDaoCallsWithParameter(); inst.getCallTimeByMethodGroup(); inst.getCallFrequency(null); inst.getCallFrequency("get_DAO"); inst.getConcurrentCalls(); System.out.println("Done."); } catch (Throwable t) { t.printStackTrace(); } } }