1fb3fb4f3Stomee /* 2fb3fb4f3Stomee * CDDL HEADER START 3fb3fb4f3Stomee * 4fb3fb4f3Stomee * The contents of this file are subject to the terms of the 5fb3fb4f3Stomee * Common Development and Distribution License (the "License"). 6fb3fb4f3Stomee * You may not use this file except in compliance with the License. 7fb3fb4f3Stomee * 8fb3fb4f3Stomee * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9fb3fb4f3Stomee * or http://www.opensolaris.org/os/licensing. 10fb3fb4f3Stomee * See the License for the specific language governing permissions 11fb3fb4f3Stomee * and limitations under the License. 12fb3fb4f3Stomee * 13fb3fb4f3Stomee * When distributing Covered Code, include this CDDL HEADER in each 14fb3fb4f3Stomee * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15fb3fb4f3Stomee * If applicable, add the following below this CDDL HEADER, with the 16fb3fb4f3Stomee * fields enclosed by brackets "[]" replaced with your own identifying 17fb3fb4f3Stomee * information: Portions Copyright [yyyy] [name of copyright owner] 18fb3fb4f3Stomee * 19fb3fb4f3Stomee * CDDL HEADER END 20fb3fb4f3Stomee */ 21fb3fb4f3Stomee 22fb3fb4f3Stomee /* 23e77b06d2Stomee * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24fb3fb4f3Stomee * Use is subject to license terms. 25fb3fb4f3Stomee */ 26fb3fb4f3Stomee package org.opensolaris.os.dtrace; 27fb3fb4f3Stomee 28fb3fb4f3Stomee import java.util.*; 29fb3fb4f3Stomee import java.io.*; 30fb3fb4f3Stomee import java.beans.*; 31fb3fb4f3Stomee 32fb3fb4f3Stomee /** 33fb3fb4f3Stomee * Data generated when a DTrace probe fires, contains one record for 34fb3fb4f3Stomee * every record-generating action in the probe. (Some D actions, such 35fb3fb4f3Stomee * as {@code clear()}, do not generate a {@code ProbeData} record.) A 36fb3fb4f3Stomee * {@link Consumer} gets data from DTrace by registering a {@link 37fb3fb4f3Stomee * ConsumerListener listener} to get probe data whenever a probe fires: 38fb3fb4f3Stomee * <pre><code> 39fb3fb4f3Stomee * Consumer consumer = new LocalConsumer(); 40fb3fb4f3Stomee * consumer.addConsumerListener(new ConsumerAdapter() { 41fb3fb4f3Stomee * public void dataReceived(DataEvent e) { 42fb3fb4f3Stomee * ProbeData probeData = e.getProbeData(); 43fb3fb4f3Stomee * System.out.println(probeData); 44fb3fb4f3Stomee * } 45fb3fb4f3Stomee * }); 46fb3fb4f3Stomee * </code></pre> 47fb3fb4f3Stomee * Getting DTrace to generate that probe data involves compiling, 48fb3fb4f3Stomee * enabling, and running a D program: 49fb3fb4f3Stomee * <pre><code> 50fb3fb4f3Stomee * try { 51fb3fb4f3Stomee * consumer.open(); 52fb3fb4f3Stomee * consumer.compile(program); 53fb3fb4f3Stomee * consumer.enable(); // instruments code at matching probe points 54fb3fb4f3Stomee * consumer.go(); // non-blocking; generates probe data in background 55fb3fb4f3Stomee * } catch (DTraceException e) { 56fb3fb4f3Stomee * e.printStackTrace(); 57fb3fb4f3Stomee * } 58fb3fb4f3Stomee * </code></pre> 59fb3fb4f3Stomee * Currently the {@code ProbeData} instance does not record a timestamp. 60fb3fb4f3Stomee * If you need a timestamp, trace the built-in {@code timestamp} 61fb3fb4f3Stomee * variable in your D program. (See the 62*3a931819SPeter Tribble * <a href=http://dtrace.org/guide/chp-variables.html#chp-variables-5> 63fb3fb4f3Stomee * <b>Built-in Variables</b></a> section of the <b>Variables</b> chapter of 64*3a931819SPeter Tribble * the <i>Dynamic Tracing Guide</i>). 65fb3fb4f3Stomee * <p> 66fb3fb4f3Stomee * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. 67fb3fb4f3Stomee * 68fb3fb4f3Stomee * @see Consumer#addConsumerListener(ConsumerListener l) 69fb3fb4f3Stomee * @see ConsumerListener#dataReceived(DataEvent e) 70fb3fb4f3Stomee * 71fb3fb4f3Stomee * @author Tom Erickson 72fb3fb4f3Stomee */ 73fb3fb4f3Stomee public final class ProbeData implements Serializable, Comparable <ProbeData> { 74fb3fb4f3Stomee static final long serialVersionUID = -7021504416192099215L; 75fb3fb4f3Stomee 76fb3fb4f3Stomee static { 77fb3fb4f3Stomee try { 78fb3fb4f3Stomee BeanInfo info = Introspector.getBeanInfo(ProbeData.class); 79fb3fb4f3Stomee PersistenceDelegate persistenceDelegate = 80fb3fb4f3Stomee new DefaultPersistenceDelegate( 81fb3fb4f3Stomee new String[] {"enabledProbeID", "CPU", 82fb3fb4f3Stomee "enabledProbeDescription", "flow", "records"}); 83fb3fb4f3Stomee BeanDescriptor d = info.getBeanDescriptor(); 84fb3fb4f3Stomee d.setValue("persistenceDelegate", persistenceDelegate); 85fb3fb4f3Stomee } catch (IntrospectionException e) { 86fb3fb4f3Stomee System.out.println(e); 87fb3fb4f3Stomee } 88fb3fb4f3Stomee } 89fb3fb4f3Stomee 90fb3fb4f3Stomee private static Comparator <ProbeData> DEFAULT_CMP; 91fb3fb4f3Stomee 92fb3fb4f3Stomee static { 93fb3fb4f3Stomee try { 94fb3fb4f3Stomee DEFAULT_CMP = ProbeData.getComparator(KeyField.RECORDS, 95fb3fb4f3Stomee KeyField.EPID); 96fb3fb4f3Stomee } catch (Throwable e) { 97fb3fb4f3Stomee e.printStackTrace(); 98fb3fb4f3Stomee System.exit(1); 99fb3fb4f3Stomee } 100fb3fb4f3Stomee } 101fb3fb4f3Stomee 102fb3fb4f3Stomee /** @serial */ 103fb3fb4f3Stomee private int epid; 104fb3fb4f3Stomee /** @serial */ 105fb3fb4f3Stomee private int cpu; 106fb3fb4f3Stomee /** @serial */ 107fb3fb4f3Stomee private ProbeDescription enabledProbeDescription; 108fb3fb4f3Stomee /** @serial */ 109fb3fb4f3Stomee private Flow flow; 110fb3fb4f3Stomee // Scratch data, one element per native probedata->dtpda_edesc->dtepd_nrecs 111fb3fb4f3Stomee // element, cleared after records list is fully populated. 112127bbe13Stomee private transient List <Record> nativeElements; 113fb3fb4f3Stomee /** @serial */ 114fb3fb4f3Stomee private List <Record> records; 115fb3fb4f3Stomee 116fb3fb4f3Stomee /** 117fb3fb4f3Stomee * Enumerates the fields by which {@link ProbeData} may be sorted 118fb3fb4f3Stomee * using the {@link #getComparator(KeyField[] f) getComparator()} 119fb3fb4f3Stomee * convenience method. 120fb3fb4f3Stomee */ 121fb3fb4f3Stomee public enum KeyField { 122fb3fb4f3Stomee /** Specifies {@link ProbeData#getCPU()} */ 123fb3fb4f3Stomee CPU, 124fb3fb4f3Stomee /** Specifies {@link ProbeData#getEnabledProbeDescription()} */ 125fb3fb4f3Stomee PROBE, 126fb3fb4f3Stomee /** Specifies {@link ProbeData#getEnabledProbeID()} */ 127fb3fb4f3Stomee EPID, 128fb3fb4f3Stomee /** Specifies {@link ProbeData#getRecords()} */ 129fb3fb4f3Stomee RECORDS 130fb3fb4f3Stomee } 131fb3fb4f3Stomee 132fb3fb4f3Stomee /** 133fb3fb4f3Stomee * Called by native code. 134fb3fb4f3Stomee */ 135fb3fb4f3Stomee private ProbeData(int enabledProbeID, int cpuID, ProbeDescription p, Flow f, int nativeElementCount)136fb3fb4f3Stomee ProbeData(int enabledProbeID, int cpuID, ProbeDescription p, 137fb3fb4f3Stomee Flow f, int nativeElementCount) 138fb3fb4f3Stomee { 139fb3fb4f3Stomee epid = enabledProbeID; 140fb3fb4f3Stomee cpu = cpuID; 141fb3fb4f3Stomee enabledProbeDescription = p; 142fb3fb4f3Stomee flow = f; 143127bbe13Stomee nativeElements = new ArrayList <Record> (nativeElementCount); 144fb3fb4f3Stomee records = new ArrayList <Record> (); 145fb3fb4f3Stomee validate(); 146fb3fb4f3Stomee } 147fb3fb4f3Stomee 148fb3fb4f3Stomee /** 149fb3fb4f3Stomee * Creates a probe data instance with the given properties and list 150fb3fb4f3Stomee * of records. Supports XML persistence. 151fb3fb4f3Stomee * 152fb3fb4f3Stomee * @param enabledProbeID identifies the enabled probe that fired; 153fb3fb4f3Stomee * the ID is generated by the native DTrace library to distinguish 154fb3fb4f3Stomee * all probes enabled by the source consumer (as opposed to 155fb3fb4f3Stomee * all probes on the system) 156fb3fb4f3Stomee * @param cpuID non-negative ID, identifies the CPU on which the 157fb3fb4f3Stomee * probe fired 158fb3fb4f3Stomee * @param p identifies the enabled probe that fired 159fb3fb4f3Stomee * @param f current state of control flow (entry or return and depth 160fb3fb4f3Stomee * in call stack) at time of probe firing, included if {@link 161fb3fb4f3Stomee * Option#flowindent flowindent} option used, {@code null} otherwise 162fb3fb4f3Stomee * @param recordList list of records generated by D actions in the 163fb3fb4f3Stomee * probe that fired, one record per action, may be empty 164fb3fb4f3Stomee * @throws NullPointerException if the given probe description or 165fb3fb4f3Stomee * list of records is {@code null} 166fb3fb4f3Stomee */ 167fb3fb4f3Stomee public ProbeData(int enabledProbeID, int cpuID, ProbeDescription p, Flow f, List <Record> recordList)168fb3fb4f3Stomee ProbeData(int enabledProbeID, int cpuID, ProbeDescription p, 169fb3fb4f3Stomee Flow f, List <Record> recordList) 170fb3fb4f3Stomee { 171fb3fb4f3Stomee epid = enabledProbeID; 172fb3fb4f3Stomee cpu = cpuID; 173fb3fb4f3Stomee enabledProbeDescription = p; 174fb3fb4f3Stomee flow = f; 175fb3fb4f3Stomee records = new ArrayList <Record> (recordList.size()); 176fb3fb4f3Stomee records.addAll(recordList); 177fb3fb4f3Stomee validate(); 178fb3fb4f3Stomee } 179fb3fb4f3Stomee 18091cfa10aStomee private final void validate()181fb3fb4f3Stomee validate() 182fb3fb4f3Stomee { 183fb3fb4f3Stomee if (enabledProbeDescription == null) { 184fb3fb4f3Stomee throw new NullPointerException( 185fb3fb4f3Stomee "enabled probe description is null"); 186fb3fb4f3Stomee } 187fb3fb4f3Stomee if (records == null) { 188fb3fb4f3Stomee throw new NullPointerException("record list is null"); 189fb3fb4f3Stomee } 190fb3fb4f3Stomee } 191fb3fb4f3Stomee 192fb3fb4f3Stomee private void addDataElement(Record o)193127bbe13Stomee addDataElement(Record o) 194fb3fb4f3Stomee { 195127bbe13Stomee // Early error detection if native code adds the wrong type 196127bbe13Stomee Record r = Record.class.cast(o); 197127bbe13Stomee 198fb3fb4f3Stomee nativeElements.add(o); 199fb3fb4f3Stomee } 200fb3fb4f3Stomee 201fb3fb4f3Stomee /** 202fb3fb4f3Stomee * Called by native code. 203fb3fb4f3Stomee */ 204fb3fb4f3Stomee private void addRecord(Record record)205fb3fb4f3Stomee addRecord(Record record) 206fb3fb4f3Stomee { 207fb3fb4f3Stomee records.add(record); 208fb3fb4f3Stomee } 209fb3fb4f3Stomee 210fb3fb4f3Stomee /** 211fb3fb4f3Stomee * Called by native code. 212fb3fb4f3Stomee */ 213fb3fb4f3Stomee private void addTraceRecord(int i)214fb3fb4f3Stomee addTraceRecord(int i) 215fb3fb4f3Stomee { 216fb3fb4f3Stomee // trace() value is preceded by one null for every D program 217fb3fb4f3Stomee // statement preceding trace() that is not a D action, such as 218fb3fb4f3Stomee // assignment to a variable (results in a native probedata 219fb3fb4f3Stomee // record with no data). 220fb3fb4f3Stomee int len = nativeElements.size(); 221127bbe13Stomee Record rec = null; 222127bbe13Stomee for (; ((rec = nativeElements.get(i)) == null) && (i < len); ++i); 223127bbe13Stomee records.add(rec); 224127bbe13Stomee } 225127bbe13Stomee 226127bbe13Stomee /** 227127bbe13Stomee * Called by native code. 228127bbe13Stomee */ 229127bbe13Stomee private void addSymbolRecord(int i, String lookupString)230127bbe13Stomee addSymbolRecord(int i, String lookupString) 231127bbe13Stomee { 232127bbe13Stomee int len = nativeElements.size(); 233127bbe13Stomee Record rec = null; 234127bbe13Stomee for (; ((rec = nativeElements.get(i)) == null) && (i < len); ++i); 235127bbe13Stomee SymbolValueRecord symbol = SymbolValueRecord.class.cast(rec); 236127bbe13Stomee if (symbol instanceof KernelSymbolRecord) { 237127bbe13Stomee KernelSymbolRecord.class.cast(symbol).setSymbol(lookupString); 238127bbe13Stomee } else if (symbol instanceof UserSymbolRecord) { 239127bbe13Stomee UserSymbolRecord.class.cast(symbol).setSymbol(lookupString); 240127bbe13Stomee } else { 241127bbe13Stomee throw new IllegalStateException("no symbol record at index " + i); 242127bbe13Stomee } 243127bbe13Stomee records.add(symbol); 244fb3fb4f3Stomee } 245fb3fb4f3Stomee 246fb3fb4f3Stomee /** 247fb3fb4f3Stomee * Called by native code. 248fb3fb4f3Stomee */ 249fb3fb4f3Stomee private void addStackRecord(int i, String framesString)250fb3fb4f3Stomee addStackRecord(int i, String framesString) 251fb3fb4f3Stomee { 252fb3fb4f3Stomee int len = nativeElements.size(); 253127bbe13Stomee Record rec = null; 254127bbe13Stomee for (; ((rec = nativeElements.get(i)) == null) && (i < len); ++i); 255127bbe13Stomee StackValueRecord stack = StackValueRecord.class.cast(rec); 256fb3fb4f3Stomee StackFrame[] frames = KernelStackRecord.parse(framesString); 257fb3fb4f3Stomee if (stack instanceof KernelStackRecord) { 258127bbe13Stomee KernelStackRecord.class.cast(stack).setStackFrames(frames); 259fb3fb4f3Stomee } else if (stack instanceof UserStackRecord) { 260127bbe13Stomee UserStackRecord.class.cast(stack).setStackFrames(frames); 261fb3fb4f3Stomee } else { 262fb3fb4f3Stomee throw new IllegalStateException("no stack record at index " + i); 263fb3fb4f3Stomee } 264fb3fb4f3Stomee records.add(stack); 265fb3fb4f3Stomee } 266fb3fb4f3Stomee 267fb3fb4f3Stomee /** 268fb3fb4f3Stomee * Called by native code. 269fb3fb4f3Stomee */ 270fb3fb4f3Stomee private void addPrintfRecord()271fb3fb4f3Stomee addPrintfRecord() 272fb3fb4f3Stomee { 273fb3fb4f3Stomee records.add(new PrintfRecord()); 274fb3fb4f3Stomee } 275fb3fb4f3Stomee 276fb3fb4f3Stomee /** 277fb3fb4f3Stomee * Called by native code. 278fb3fb4f3Stomee */ 279fb3fb4f3Stomee private void addPrintaRecord(long snaptimeNanos, boolean isFormatString)280fb3fb4f3Stomee addPrintaRecord(long snaptimeNanos, boolean isFormatString) 281fb3fb4f3Stomee { 282fb3fb4f3Stomee records.add(new PrintaRecord(snaptimeNanos, isFormatString)); 283fb3fb4f3Stomee } 284fb3fb4f3Stomee 285fb3fb4f3Stomee private PrintaRecord getLastPrinta()286fb3fb4f3Stomee getLastPrinta() 287fb3fb4f3Stomee { 288fb3fb4f3Stomee ListIterator <Record> itr = records.listIterator(records.size()); 289fb3fb4f3Stomee PrintaRecord printa = null; 290fb3fb4f3Stomee Record record; 291fb3fb4f3Stomee while (itr.hasPrevious() && (printa == null)) { 292fb3fb4f3Stomee record = itr.previous(); 293fb3fb4f3Stomee if (record instanceof PrintaRecord) { 294127bbe13Stomee printa = PrintaRecord.class.cast(record); 295fb3fb4f3Stomee } 296fb3fb4f3Stomee } 297fb3fb4f3Stomee return printa; 298fb3fb4f3Stomee } 299fb3fb4f3Stomee 300fb3fb4f3Stomee /** 301fb3fb4f3Stomee * Called by native code. 302fb3fb4f3Stomee */ 303fb3fb4f3Stomee private void addAggregationRecord(String aggregationName, long aggid, AggregationRecord rec)304fb3fb4f3Stomee addAggregationRecord(String aggregationName, long aggid, 305fb3fb4f3Stomee AggregationRecord rec) 306fb3fb4f3Stomee { 307fb3fb4f3Stomee PrintaRecord printa = getLastPrinta(); 308fb3fb4f3Stomee if (printa == null) { 309fb3fb4f3Stomee throw new IllegalStateException( 310fb3fb4f3Stomee "No PrintaRecord in this ProbeData"); 311fb3fb4f3Stomee } 312fb3fb4f3Stomee printa.addRecord(aggregationName, aggid, rec); 313fb3fb4f3Stomee } 314fb3fb4f3Stomee 315fb3fb4f3Stomee /** 316fb3fb4f3Stomee * Called by native code. 317fb3fb4f3Stomee */ 318fb3fb4f3Stomee private void invalidatePrintaRecord()319fb3fb4f3Stomee invalidatePrintaRecord() 320fb3fb4f3Stomee { 321fb3fb4f3Stomee PrintaRecord printa = getLastPrinta(); 322fb3fb4f3Stomee if (printa == null) { 323fb3fb4f3Stomee throw new IllegalStateException( 324fb3fb4f3Stomee "No PrintaRecord in this ProbeData"); 325fb3fb4f3Stomee } 326fb3fb4f3Stomee printa.invalidate(); 327fb3fb4f3Stomee } 328fb3fb4f3Stomee 329fb3fb4f3Stomee /** 330fb3fb4f3Stomee * Called by native code. 331fb3fb4f3Stomee */ 332fb3fb4f3Stomee private void addPrintaFormattedString(Tuple tuple, String s)333fb3fb4f3Stomee addPrintaFormattedString(Tuple tuple, String s) 334fb3fb4f3Stomee { 335fb3fb4f3Stomee PrintaRecord printa = getLastPrinta(); 336fb3fb4f3Stomee if (printa == null) { 337fb3fb4f3Stomee throw new IllegalStateException( 338fb3fb4f3Stomee "No PrintaRecord in this ProbeData"); 339fb3fb4f3Stomee } 340fb3fb4f3Stomee printa.addFormattedString(tuple, s); 341fb3fb4f3Stomee } 342fb3fb4f3Stomee 343fb3fb4f3Stomee /** 344fb3fb4f3Stomee * Called by native code. 345fb3fb4f3Stomee */ 346fb3fb4f3Stomee private void addExitRecord(int i)347fb3fb4f3Stomee addExitRecord(int i) 348fb3fb4f3Stomee { 349fb3fb4f3Stomee int len = nativeElements.size(); 350127bbe13Stomee Record rec = null; 351127bbe13Stomee for (; ((rec = nativeElements.get(i)) == null) && (i < len); ++i); 352127bbe13Stomee ScalarRecord scalar = ScalarRecord.class.cast(rec); 353127bbe13Stomee Integer exitStatus = Integer.class.cast(scalar.getValue()); 354fb3fb4f3Stomee records.add(new ExitRecord(exitStatus)); 355fb3fb4f3Stomee } 356fb3fb4f3Stomee 357fb3fb4f3Stomee /** 358fb3fb4f3Stomee * Called by native code. Attaches native probedata elements cached 359fb3fb4f3Stomee * between the given first index and last index inclusive to the most 360fb3fb4f3Stomee * recently added record if applicable. 361fb3fb4f3Stomee */ 362fb3fb4f3Stomee private void attachRecordElements(int first, int last)363fb3fb4f3Stomee attachRecordElements(int first, int last) 364fb3fb4f3Stomee { 365fb3fb4f3Stomee Record record = records.get(records.size() - 1); 366fb3fb4f3Stomee if (record instanceof PrintfRecord) { 367127bbe13Stomee PrintfRecord printf = PrintfRecord.class.cast(record); 368127bbe13Stomee Record e; 369fb3fb4f3Stomee for (int i = first; i <= last; ++i) { 370fb3fb4f3Stomee e = nativeElements.get(i); 371fb3fb4f3Stomee if (e == null) { 372fb3fb4f3Stomee // printf() unformatted elements are preceded by one 373fb3fb4f3Stomee // null for every D program statement preceding the 374fb3fb4f3Stomee // printf() that is not a D action, such as 375fb3fb4f3Stomee // assignment to a variable (generates a probedata 376fb3fb4f3Stomee // record with no data). 377fb3fb4f3Stomee continue; 378fb3fb4f3Stomee } 379127bbe13Stomee printf.addUnformattedElement(ScalarRecord.class.cast(e)); 380fb3fb4f3Stomee } 381fb3fb4f3Stomee } 382fb3fb4f3Stomee } 383fb3fb4f3Stomee 384fb3fb4f3Stomee /** 385fb3fb4f3Stomee * Called by native code. 386fb3fb4f3Stomee */ 387fb3fb4f3Stomee void clearNativeElements()388fb3fb4f3Stomee clearNativeElements() 389fb3fb4f3Stomee { 390fb3fb4f3Stomee nativeElements = null; 391fb3fb4f3Stomee } 392fb3fb4f3Stomee 393fb3fb4f3Stomee /** 394fb3fb4f3Stomee * Called by native code. 395fb3fb4f3Stomee */ 396fb3fb4f3Stomee private void setFormattedString(String s)397fb3fb4f3Stomee setFormattedString(String s) 398fb3fb4f3Stomee { 399fb3fb4f3Stomee Record record = records.get(records.size() - 1); 400fb3fb4f3Stomee if (record instanceof PrintfRecord) { 401127bbe13Stomee PrintfRecord printf = PrintfRecord.class.cast(record); 402fb3fb4f3Stomee printf.setFormattedString(s); 403fb3fb4f3Stomee } 404fb3fb4f3Stomee } 405fb3fb4f3Stomee 406fb3fb4f3Stomee /** 407fb3fb4f3Stomee * Convenience method, gets a comparator that sorts multiple {@link 408fb3fb4f3Stomee * ProbeDescription} instances by the specified field or fields. If 409fb3fb4f3Stomee * more than one sort field is specified, the probe data are sorted 410fb3fb4f3Stomee * by the first field, and in case of a tie, by the second field, 411fb3fb4f3Stomee * and so on, in the order that the fields are specified. 412fb3fb4f3Stomee * 413fb3fb4f3Stomee * @param f field specifiers given in descending order of sort 414fb3fb4f3Stomee * priority; lower priority fields are only compared (as a tie 415fb3fb4f3Stomee * breaker) when all higher priority fields are equal 416fb3fb4f3Stomee * @return non-null probe data comparator that sorts by the 417fb3fb4f3Stomee * specified sort fields in the given order 418fb3fb4f3Stomee */ 419fb3fb4f3Stomee public static Comparator <ProbeData> getComparator(KeyField .... f)420fb3fb4f3Stomee getComparator(KeyField ... f) 421fb3fb4f3Stomee { 422fb3fb4f3Stomee return new Cmp(f); 423fb3fb4f3Stomee } 424fb3fb4f3Stomee 425fb3fb4f3Stomee private static class Cmp implements Comparator <ProbeData> { 426fb3fb4f3Stomee private KeyField[] sortFields; 427fb3fb4f3Stomee 428fb3fb4f3Stomee private Cmp(KeyField .... f)429fb3fb4f3Stomee Cmp(KeyField ... f) 430fb3fb4f3Stomee { 431fb3fb4f3Stomee sortFields = f; 432fb3fb4f3Stomee } 433fb3fb4f3Stomee 434fb3fb4f3Stomee public int compare(ProbeData d1, ProbeData d2)435fb3fb4f3Stomee compare(ProbeData d1, ProbeData d2) 436fb3fb4f3Stomee { 437fb3fb4f3Stomee return ProbeData.compare(d1, d2, sortFields); 438fb3fb4f3Stomee } 439fb3fb4f3Stomee } 440fb3fb4f3Stomee 441127bbe13Stomee static int compareUnsigned(int i1, int i2)442127bbe13Stomee compareUnsigned(int i1, int i2) 443127bbe13Stomee { 444127bbe13Stomee int cmp; 445127bbe13Stomee 446127bbe13Stomee if (i1 < 0) { 447127bbe13Stomee if (i2 < 0) { 448127bbe13Stomee cmp = (i1 < i2 ? -1 : (i1 > i2 ? 1 : 0)); 449127bbe13Stomee } else { 450127bbe13Stomee cmp = 1; // negative > positive 451127bbe13Stomee } 452127bbe13Stomee } else if (i2 < 0) { 453127bbe13Stomee cmp = -1; // positive < negative 454127bbe13Stomee } else { 455127bbe13Stomee cmp = (i1 < i2 ? -1 : (i1 > i2 ? 1 : 0)); 456127bbe13Stomee } 457127bbe13Stomee 458127bbe13Stomee return cmp; 459127bbe13Stomee } 460127bbe13Stomee 461127bbe13Stomee static int compareUnsigned(long i1, long i2)462127bbe13Stomee compareUnsigned(long i1, long i2) 463127bbe13Stomee { 464127bbe13Stomee int cmp; 465127bbe13Stomee 466127bbe13Stomee if (i1 < 0) { 467127bbe13Stomee if (i2 < 0) { 468127bbe13Stomee cmp = (i1 < i2 ? -1 : (i1 > i2 ? 1 : 0)); 469127bbe13Stomee } else { 470127bbe13Stomee cmp = 1; // negative > positive 471127bbe13Stomee } 472127bbe13Stomee } else if (i2 < 0) { 473127bbe13Stomee cmp = -1; // positive < negative 474127bbe13Stomee } else { 475127bbe13Stomee cmp = (i1 < i2 ? -1 : (i1 > i2 ? 1 : 0)); 476127bbe13Stomee } 477127bbe13Stomee 478127bbe13Stomee return cmp; 479127bbe13Stomee } 480127bbe13Stomee 481127bbe13Stomee static int compareUnsigned(byte i1, byte i2)482127bbe13Stomee compareUnsigned(byte i1, byte i2) 483127bbe13Stomee { 484127bbe13Stomee int cmp; 485127bbe13Stomee 486127bbe13Stomee if (i1 < 0) { 487127bbe13Stomee if (i2 < 0) { 488127bbe13Stomee cmp = (i1 < i2 ? -1 : (i1 > i2 ? 1 : 0)); 489127bbe13Stomee } else { 490127bbe13Stomee cmp = 1; // negative > positive 491127bbe13Stomee } 492127bbe13Stomee } else if (i2 < 0) { 493127bbe13Stomee cmp = -1; // positive < negative 494127bbe13Stomee } else { 495127bbe13Stomee cmp = (i1 < i2 ? -1 : (i1 > i2 ? 1 : 0)); 496127bbe13Stomee } 497127bbe13Stomee 498127bbe13Stomee return cmp; 499127bbe13Stomee } 500127bbe13Stomee 501127bbe13Stomee static int compareByteArrays(byte[] a1, byte[] a2)502127bbe13Stomee compareByteArrays(byte[] a1, byte[] a2) 503127bbe13Stomee { 504127bbe13Stomee int cmp = 0; 505127bbe13Stomee int len1 = a1.length; 506127bbe13Stomee int len2 = a2.length; 507127bbe13Stomee 508127bbe13Stomee for (int i = 0; (cmp == 0) && (i < len1) && (i < len2); ++i) { 509127bbe13Stomee cmp = compareUnsigned(a1[i], a2[i]); 510127bbe13Stomee } 511127bbe13Stomee 512127bbe13Stomee if (cmp == 0) { 513127bbe13Stomee cmp = (len1 < len2 ? -1 : (len1 > len2 ? 1 : 0)); 514127bbe13Stomee } 515127bbe13Stomee 516127bbe13Stomee return cmp; 517127bbe13Stomee } 518127bbe13Stomee 519127bbe13Stomee @SuppressWarnings("unchecked") 520127bbe13Stomee static int compareUnsigned(Comparable v1, Comparable v2)521127bbe13Stomee compareUnsigned(Comparable v1, Comparable v2) 522127bbe13Stomee { 523127bbe13Stomee int cmp; 524127bbe13Stomee 525127bbe13Stomee if (v1 instanceof Integer) { 526127bbe13Stomee int i1 = Integer.class.cast(v1); 527127bbe13Stomee int i2 = Integer.class.cast(v2); 528127bbe13Stomee cmp = compareUnsigned(i1, i2); 529127bbe13Stomee } else if (v1 instanceof Long) { 530127bbe13Stomee long i1 = Long.class.cast(v1); 531127bbe13Stomee long i2 = Long.class.cast(v2); 532127bbe13Stomee cmp = compareUnsigned(i1, i2); 533127bbe13Stomee } else { 534127bbe13Stomee cmp = v1.compareTo(v2); 535127bbe13Stomee } 536127bbe13Stomee 537127bbe13Stomee return cmp; 538127bbe13Stomee } 539127bbe13Stomee 540fb3fb4f3Stomee /** 541127bbe13Stomee * @throws ClassCastException if records or their data are not 542fb3fb4f3Stomee * mutually comparable 543fb3fb4f3Stomee */ 544fb3fb4f3Stomee @SuppressWarnings("unchecked") 545fb3fb4f3Stomee private static int compareRecords(Record r1, Record r2)546fb3fb4f3Stomee compareRecords(Record r1, Record r2) 547fb3fb4f3Stomee { 548fb3fb4f3Stomee int cmp; 549fb3fb4f3Stomee if (r1 instanceof ScalarRecord) { 550fb3fb4f3Stomee ScalarRecord t1 = ScalarRecord.class.cast(r1); 551fb3fb4f3Stomee ScalarRecord t2 = ScalarRecord.class.cast(r2); 552127bbe13Stomee Object o1 = t1.getValue(); 553127bbe13Stomee Object o2 = t2.getValue(); 554127bbe13Stomee if (o1 instanceof byte[]) { 555127bbe13Stomee byte[] a1 = byte[].class.cast(o1); 556127bbe13Stomee byte[] a2 = byte[].class.cast(o2); 557127bbe13Stomee cmp = compareByteArrays(a1, a2); 558127bbe13Stomee } else { 559127bbe13Stomee Comparable v1 = Comparable.class.cast(o1); 560127bbe13Stomee Comparable v2 = Comparable.class.cast(o2); 561127bbe13Stomee cmp = v1.compareTo(v2); // compare signed values 562127bbe13Stomee } 563127bbe13Stomee } else if (r1 instanceof Comparable) { 564127bbe13Stomee // StackValueRecord, SymbolValueRecord 565127bbe13Stomee Comparable v1 = Comparable.class.cast(r1); 566127bbe13Stomee Comparable v2 = Comparable.class.cast(r2); 567fb3fb4f3Stomee cmp = v1.compareTo(v2); 568fb3fb4f3Stomee } else if (r1 instanceof ExitRecord) { 569fb3fb4f3Stomee ExitRecord e1 = ExitRecord.class.cast(r1); 570fb3fb4f3Stomee ExitRecord e2 = ExitRecord.class.cast(r2); 571fb3fb4f3Stomee int status1 = e1.getStatus(); 572fb3fb4f3Stomee int status2 = e2.getStatus(); 573fb3fb4f3Stomee cmp = (status1 < status2 ? -1 : (status1 > status2 ? 1 : 0)); 574fb3fb4f3Stomee } else { 575127bbe13Stomee // PrintfRecord, PrintaRecord 576127bbe13Stomee r1.getClass().cast(r2); 577127bbe13Stomee String s1 = r1.toString(); 578127bbe13Stomee String s2 = r2.toString(); 579127bbe13Stomee cmp = s1.compareTo(s2); 580fb3fb4f3Stomee } 581fb3fb4f3Stomee 582fb3fb4f3Stomee return cmp; 583fb3fb4f3Stomee } 584fb3fb4f3Stomee 585fb3fb4f3Stomee /** 586fb3fb4f3Stomee * @throws ClassCastException if lists are not mutually comparable 587fb3fb4f3Stomee * because corresponding list elements are not comparable or the 588fb3fb4f3Stomee * list themselves are different lengths 589fb3fb4f3Stomee */ 590fb3fb4f3Stomee private static int compareRecordLists(ProbeData d1, ProbeData d2)591fb3fb4f3Stomee compareRecordLists(ProbeData d1, ProbeData d2) 592fb3fb4f3Stomee { 593fb3fb4f3Stomee List <Record> list1 = d1.getRecords(); 594fb3fb4f3Stomee List <Record> list2 = d2.getRecords(); 595fb3fb4f3Stomee int len1 = list1.size(); 596fb3fb4f3Stomee int len2 = list2.size(); 597fb3fb4f3Stomee if (len1 != len2) { 598fb3fb4f3Stomee throw new ClassCastException("Record lists of different " + 599fb3fb4f3Stomee "length are not comparable (lengths are " + 600fb3fb4f3Stomee len1 + " and " + len2 + ")."); 601fb3fb4f3Stomee } 602fb3fb4f3Stomee 603fb3fb4f3Stomee int cmp; 604fb3fb4f3Stomee Record r1; 605fb3fb4f3Stomee Record r2; 606fb3fb4f3Stomee 607fb3fb4f3Stomee for (int i = 0; (i < len1) && (i < len2); ++i) { 608fb3fb4f3Stomee r1 = list1.get(i); 609fb3fb4f3Stomee r2 = list2.get(i); 610fb3fb4f3Stomee 611fb3fb4f3Stomee cmp = compareRecords(r1, r2); 612fb3fb4f3Stomee if (cmp != 0) { 613fb3fb4f3Stomee return cmp; 614fb3fb4f3Stomee } 615fb3fb4f3Stomee } 616fb3fb4f3Stomee 617fb3fb4f3Stomee return 0; 618fb3fb4f3Stomee } 619fb3fb4f3Stomee 620fb3fb4f3Stomee private static int compare(ProbeData d1, ProbeData d2, KeyField[] comparedFields)621fb3fb4f3Stomee compare(ProbeData d1, ProbeData d2, KeyField[] comparedFields) 622fb3fb4f3Stomee { 623fb3fb4f3Stomee int cmp; 624fb3fb4f3Stomee for (KeyField f : comparedFields) { 625fb3fb4f3Stomee switch (f) { 626fb3fb4f3Stomee case CPU: 627fb3fb4f3Stomee int cpu1 = d1.getCPU(); 628fb3fb4f3Stomee int cpu2 = d2.getCPU(); 629fb3fb4f3Stomee cmp = (cpu1 < cpu2 ? -1 : (cpu1 > cpu2 ? 1 : 0)); 630fb3fb4f3Stomee break; 631fb3fb4f3Stomee case PROBE: 632fb3fb4f3Stomee ProbeDescription p1 = d1.getEnabledProbeDescription(); 633fb3fb4f3Stomee ProbeDescription p2 = d2.getEnabledProbeDescription(); 634fb3fb4f3Stomee cmp = p1.compareTo(p2); 635fb3fb4f3Stomee break; 636fb3fb4f3Stomee case EPID: 637fb3fb4f3Stomee int epid1 = d1.getEnabledProbeID(); 638fb3fb4f3Stomee int epid2 = d2.getEnabledProbeID(); 639fb3fb4f3Stomee cmp = (epid1 < epid2 ? -1 : (epid1 > epid2 ? 1 : 0)); 640fb3fb4f3Stomee break; 641fb3fb4f3Stomee case RECORDS: 642fb3fb4f3Stomee cmp = compareRecordLists(d1, d2); 643fb3fb4f3Stomee break; 644fb3fb4f3Stomee default: 645fb3fb4f3Stomee throw new IllegalArgumentException( 646fb3fb4f3Stomee "Unexpected sort field " + f); 647fb3fb4f3Stomee } 648fb3fb4f3Stomee 649fb3fb4f3Stomee if (cmp != 0) { 650fb3fb4f3Stomee return cmp; 651fb3fb4f3Stomee } 652fb3fb4f3Stomee } 653fb3fb4f3Stomee 654fb3fb4f3Stomee return 0; 655fb3fb4f3Stomee } 656fb3fb4f3Stomee 657fb3fb4f3Stomee /** 658fb3fb4f3Stomee * Gets the enabled probe ID. Identifies the enabled probe that 659fb3fb4f3Stomee * fired and generated this {@code ProbeData}. The "epid" is 660fb3fb4f3Stomee * different from {@link ProbeDescription#getID()} in that it 661fb3fb4f3Stomee * identifies a probe among all probes enabled by the source {@link 662fb3fb4f3Stomee * Consumer}, rather than among all the probes on the system. 663fb3fb4f3Stomee * 664fb3fb4f3Stomee * @return the enabled probe ID generated by the native DTrace 665fb3fb4f3Stomee * library 666fb3fb4f3Stomee */ 667fb3fb4f3Stomee public int getEnabledProbeID()668fb3fb4f3Stomee getEnabledProbeID() 669fb3fb4f3Stomee { 670fb3fb4f3Stomee return epid; 671fb3fb4f3Stomee } 672fb3fb4f3Stomee 673fb3fb4f3Stomee /** 674fb3fb4f3Stomee * Gets the ID of the CPU on which the probe fired. 675fb3fb4f3Stomee * 676fb3fb4f3Stomee * @return ID of the CPU on which the probe fired 677fb3fb4f3Stomee */ 678fb3fb4f3Stomee public int getCPU()679fb3fb4f3Stomee getCPU() 680fb3fb4f3Stomee { 681fb3fb4f3Stomee return cpu; 682fb3fb4f3Stomee } 683fb3fb4f3Stomee 684fb3fb4f3Stomee /** 685fb3fb4f3Stomee * Gets the enabled probe description. Identifies the enabled probe 686fb3fb4f3Stomee * that fired and generated this {@code ProbeData}. 687fb3fb4f3Stomee * 688fb3fb4f3Stomee * @return non-null probe description 689fb3fb4f3Stomee */ 690fb3fb4f3Stomee public ProbeDescription getEnabledProbeDescription()691fb3fb4f3Stomee getEnabledProbeDescription() 692fb3fb4f3Stomee { 693fb3fb4f3Stomee return enabledProbeDescription; 694fb3fb4f3Stomee } 695fb3fb4f3Stomee 696fb3fb4f3Stomee /** 697fb3fb4f3Stomee * Gets the current state of control flow (function entry or return, 698fb3fb4f3Stomee * and depth in call stack) at the time of the probe firing that 699fb3fb4f3Stomee * generated this {@code ProbeData} instance, or {@code null} if 700fb3fb4f3Stomee * such information was not requested with the {@code flowindent} 701fb3fb4f3Stomee * option. 702fb3fb4f3Stomee * 703fb3fb4f3Stomee * @return a description of control flow across function boundaries, 704fb3fb4f3Stomee * or {@code null} if {@code Consumer.getOption(Option.flowindent)} 705fb3fb4f3Stomee * returns {@link Option#UNSET} 706fb3fb4f3Stomee * @see Consumer#setOption(String option) 707fb3fb4f3Stomee * @see Option#flowindent 708fb3fb4f3Stomee */ 709fb3fb4f3Stomee public Flow getFlow()710fb3fb4f3Stomee getFlow() 711fb3fb4f3Stomee { 712fb3fb4f3Stomee return flow; 713fb3fb4f3Stomee } 714fb3fb4f3Stomee 715fb3fb4f3Stomee /** 716fb3fb4f3Stomee * Gets the records generated by the actions of the probe that 717fb3fb4f3Stomee * fired, in the same order as the actions that generated the 718fb3fb4f3Stomee * records. The returned list includes one record for every 719fb3fb4f3Stomee * record-generating D action (some D actions, such as {@code 720fb3fb4f3Stomee * clear()}, do not generate records). 721fb3fb4f3Stomee * 722fb3fb4f3Stomee * @return non-null, unmodifiable list view of the records belonging 723fb3fb4f3Stomee * to this {@code ProbeData} in the order of the actions in the 724fb3fb4f3Stomee * DTrace probe that generated them (record-producing actions are 725fb3fb4f3Stomee * generally those that produce output, such as {@code printf()}, 726fb3fb4f3Stomee * but also the {@code exit()} action) 727fb3fb4f3Stomee */ 728fb3fb4f3Stomee public List <Record> getRecords()729fb3fb4f3Stomee getRecords() 730fb3fb4f3Stomee { 731e77b06d2Stomee return Collections. <Record> unmodifiableList(records); 732fb3fb4f3Stomee } 733fb3fb4f3Stomee 734fb3fb4f3Stomee /** 735fb3fb4f3Stomee * Natural ordering of probe data. Sorts probe data by records 736fb3fb4f3Stomee * first, then if record data is equal, by enabled probe ID. 737fb3fb4f3Stomee * 738fb3fb4f3Stomee * @param d probe data to be compared with this probe data 739fb3fb4f3Stomee * @return a negative number, zero, or a positive number as this 740fb3fb4f3Stomee * probe data is less than, equal to, or greater than the given 741fb3fb4f3Stomee * probe data 742fb3fb4f3Stomee * @see ProbeData#getComparator(KeyField[] f) 743fb3fb4f3Stomee * @throws NullPointerException if the given probe data is 744fb3fb4f3Stomee * {@code null} 745fb3fb4f3Stomee * @throws ClassCastException if record lists of both {@code 746fb3fb4f3Stomee * ProbeData} instances are not mutually comparable because 747fb3fb4f3Stomee * corresponding list elements are not comparable or the lists 748fb3fb4f3Stomee * themselves are different lengths 749fb3fb4f3Stomee */ 750fb3fb4f3Stomee public int compareTo(ProbeData d)751fb3fb4f3Stomee compareTo(ProbeData d) 752fb3fb4f3Stomee { 753fb3fb4f3Stomee return DEFAULT_CMP.compare(this, d); 754fb3fb4f3Stomee } 755fb3fb4f3Stomee 756fb3fb4f3Stomee private void readObject(ObjectInputStream s)757fb3fb4f3Stomee readObject(ObjectInputStream s) 758fb3fb4f3Stomee throws IOException, ClassNotFoundException 759fb3fb4f3Stomee { 760fb3fb4f3Stomee s.defaultReadObject(); 761fb3fb4f3Stomee // Defensively copy record list _before_ validating. 762fb3fb4f3Stomee int len = records.size(); 763fb3fb4f3Stomee ArrayList <Record> copy = new ArrayList <Record> (len); 764fb3fb4f3Stomee copy.addAll(records); 765fb3fb4f3Stomee records = copy; 766fb3fb4f3Stomee // Check class invariants 767fb3fb4f3Stomee try { 768fb3fb4f3Stomee validate(); 769fb3fb4f3Stomee } catch (Exception e) { 7704ae67516Stomee InvalidObjectException x = new InvalidObjectException( 7714ae67516Stomee e.getMessage()); 7724ae67516Stomee x.initCause(e); 7734ae67516Stomee throw x; 774fb3fb4f3Stomee } 775fb3fb4f3Stomee } 776fb3fb4f3Stomee 777fb3fb4f3Stomee /** 778fb3fb4f3Stomee * Gets a string representation of this {@code ProbeData} instance 779fb3fb4f3Stomee * useful for logging and not intended for display. The exact 780fb3fb4f3Stomee * details of the representation are unspecified and subject to 781fb3fb4f3Stomee * change, but the following format may be regarded as typical: 782fb3fb4f3Stomee * <pre><code> 783fb3fb4f3Stomee * class-name[property1 = value1, property2 = value2] 784fb3fb4f3Stomee * </code></pre> 785fb3fb4f3Stomee */ 786fb3fb4f3Stomee public String toString()787fb3fb4f3Stomee toString() 788fb3fb4f3Stomee { 7894ae67516Stomee StringBuilder buf = new StringBuilder(); 790fb3fb4f3Stomee buf.append(ProbeData.class.getName()); 791fb3fb4f3Stomee buf.append("[epid = "); 792fb3fb4f3Stomee buf.append(epid); 793fb3fb4f3Stomee buf.append(", cpu = "); 794fb3fb4f3Stomee buf.append(cpu); 795fb3fb4f3Stomee buf.append(", enabledProbeDescription = "); 796fb3fb4f3Stomee buf.append(enabledProbeDescription); 797fb3fb4f3Stomee buf.append(", flow = "); 798fb3fb4f3Stomee buf.append(flow); 799fb3fb4f3Stomee buf.append(", records = "); 800fb3fb4f3Stomee 801fb3fb4f3Stomee Record record; 802fb3fb4f3Stomee Object value; 803fb3fb4f3Stomee buf.append('['); 804fb3fb4f3Stomee for (int i = 0; i < records.size(); ++i) { 805fb3fb4f3Stomee if (i > 0) { 806fb3fb4f3Stomee buf.append(", "); 807fb3fb4f3Stomee } 808fb3fb4f3Stomee record = records.get(i); 809fb3fb4f3Stomee if (record instanceof ValueRecord) { 810127bbe13Stomee value = ValueRecord.class.cast(record).getValue(); 811fb3fb4f3Stomee if (value instanceof String) { 812fb3fb4f3Stomee buf.append("\""); 813127bbe13Stomee buf.append(String.class.cast(value)); 814fb3fb4f3Stomee buf.append("\""); 815fb3fb4f3Stomee } else { 816fb3fb4f3Stomee buf.append(record); 817fb3fb4f3Stomee } 818fb3fb4f3Stomee } else { 819fb3fb4f3Stomee buf.append(record); 820fb3fb4f3Stomee } 821fb3fb4f3Stomee } 822fb3fb4f3Stomee buf.append(']'); 823fb3fb4f3Stomee 824fb3fb4f3Stomee buf.append(']'); 825fb3fb4f3Stomee return buf.toString(); 826fb3fb4f3Stomee } 827fb3fb4f3Stomee } 828