1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 * ident "%Z%%M% %I% %E% SMI" 27 */ 28 package org.opensolaris.os.dtrace; 29 30 import java.util.*; 31 import java.io.*; 32 import java.beans.*; 33 34 /** 35 * Data generated when a DTrace probe fires, contains one record for 36 * every record-generating action in the probe. (Some D actions, such 37 * as {@code clear()}, do not generate a {@code ProbeData} record.) A 38 * {@link Consumer} gets data from DTrace by registering a {@link 39 * ConsumerListener listener} to get probe data whenever a probe fires: 40 * <pre><code> 41 * Consumer consumer = new LocalConsumer(); 42 * consumer.addConsumerListener(new ConsumerAdapter() { 43 * public void dataReceived(DataEvent e) { 44 * ProbeData probeData = e.getProbeData(); 45 * System.out.println(probeData); 46 * } 47 * }); 48 * </code></pre> 49 * Getting DTrace to generate that probe data involves compiling, 50 * enabling, and running a D program: 51 * <pre><code> 52 * try { 53 * consumer.open(); 54 * consumer.compile(program); 55 * consumer.enable(); // instruments code at matching probe points 56 * consumer.go(); // non-blocking; generates probe data in background 57 * } catch (DTraceException e) { 58 * e.printStackTrace(); 59 * } 60 * </code></pre> 61 * Currently the {@code ProbeData} instance does not record a timestamp. 62 * If you need a timestamp, trace the built-in {@code timestamp} 63 * variable in your D program. (See the 64 * <a href=http://docs.sun.com/app/docs/doc/817-6223/6mlkidlfv?a=view> 65 * <b>Built-in Variables</b></a> section of the <b>Variables</b> chapter of 66 * the <i>Solaris Dynamic Tracing Guide</i>). 67 * <p> 68 * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. 69 * 70 * @see Consumer#addConsumerListener(ConsumerListener l) 71 * @see ConsumerListener#dataReceived(DataEvent e) 72 * 73 * @author Tom Erickson 74 */ 75 public final class ProbeData implements Serializable, Comparable <ProbeData> { 76 static final long serialVersionUID = -7021504416192099215L; 77 78 static { 79 try { 80 BeanInfo info = Introspector.getBeanInfo(ProbeData.class); 81 PersistenceDelegate persistenceDelegate = 82 new DefaultPersistenceDelegate( 83 new String[] {"enabledProbeID", "CPU", 84 "enabledProbeDescription", "flow", "records"}); 85 BeanDescriptor d = info.getBeanDescriptor(); 86 d.setValue("persistenceDelegate", persistenceDelegate); 87 } catch (IntrospectionException e) { 88 System.out.println(e); 89 } 90 } 91 92 private static Comparator <ProbeData> DEFAULT_CMP; 93 94 static { 95 try { 96 DEFAULT_CMP = ProbeData.getComparator(KeyField.RECORDS, 97 KeyField.EPID); 98 } catch (Throwable e) { 99 e.printStackTrace(); 100 System.exit(1); 101 } 102 } 103 104 /** @serial */ 105 private int epid; 106 /** @serial */ 107 private int cpu; 108 /** @serial */ 109 private ProbeDescription enabledProbeDescription; 110 /** @serial */ 111 private Flow flow; 112 // Scratch data, one element per native probedata->dtpda_edesc->dtepd_nrecs 113 // element, cleared after records list is fully populated. 114 private transient List <Record> nativeElements; 115 /** @serial */ 116 private List <Record> records; 117 118 /** 119 * Enumerates the fields by which {@link ProbeData} may be sorted 120 * using the {@link #getComparator(KeyField[] f) getComparator()} 121 * convenience method. 122 */ 123 public enum KeyField { 124 /** Specifies {@link ProbeData#getCPU()} */ 125 CPU, 126 /** Specifies {@link ProbeData#getEnabledProbeDescription()} */ 127 PROBE, 128 /** Specifies {@link ProbeData#getEnabledProbeID()} */ 129 EPID, 130 /** Specifies {@link ProbeData#getRecords()} */ 131 RECORDS 132 } 133 134 /** 135 * Called by native code. 136 */ 137 private ProbeData(int enabledProbeID, int cpuID, ProbeDescription p, Flow f, int nativeElementCount)138 ProbeData(int enabledProbeID, int cpuID, ProbeDescription p, 139 Flow f, int nativeElementCount) 140 { 141 epid = enabledProbeID; 142 cpu = cpuID; 143 enabledProbeDescription = p; 144 flow = f; 145 nativeElements = new ArrayList <Record> (nativeElementCount); 146 records = new ArrayList <Record> (); 147 validate(); 148 } 149 150 /** 151 * Creates a probe data instance with the given properties and list 152 * of records. Supports XML persistence. 153 * 154 * @param enabledProbeID identifies the enabled probe that fired; 155 * the ID is generated by the native DTrace library to distinguish 156 * all probes enabled by the source consumer (as opposed to 157 * all probes on the system) 158 * @param cpuID non-negative ID, identifies the CPU on which the 159 * probe fired 160 * @param p identifies the enabled probe that fired 161 * @param f current state of control flow (entry or return and depth 162 * in call stack) at time of probe firing, included if {@link 163 * Option#flowindent flowindent} option used, {@code null} otherwise 164 * @param recordList list of records generated by D actions in the 165 * probe that fired, one record per action, may be empty 166 * @throws NullPointerException if the given probe description or 167 * list of records is {@code null} 168 */ 169 public ProbeData(int enabledProbeID, int cpuID, ProbeDescription p, Flow f, List <Record> recordList)170 ProbeData(int enabledProbeID, int cpuID, ProbeDescription p, 171 Flow f, List <Record> recordList) 172 { 173 epid = enabledProbeID; 174 cpu = cpuID; 175 enabledProbeDescription = p; 176 flow = f; 177 records = new ArrayList <Record> (recordList.size()); 178 records.addAll(recordList); 179 validate(); 180 } 181 182 private final void validate()183 validate() 184 { 185 if (enabledProbeDescription == null) { 186 throw new NullPointerException( 187 "enabled probe description is null"); 188 } 189 if (records == null) { 190 throw new NullPointerException("record list is null"); 191 } 192 } 193 194 private void addDataElement(Record o)195 addDataElement(Record o) 196 { 197 // Early error detection if native code adds the wrong type 198 Record r = Record.class.cast(o); 199 200 nativeElements.add(o); 201 } 202 203 /** 204 * Called by native code. 205 */ 206 private void addRecord(Record record)207 addRecord(Record record) 208 { 209 records.add(record); 210 } 211 212 /** 213 * Called by native code. 214 */ 215 private void addTraceRecord(int i)216 addTraceRecord(int i) 217 { 218 // trace() value is preceded by one null for every D program 219 // statement preceding trace() that is not a D action, such as 220 // assignment to a variable (results in a native probedata 221 // record with no data). 222 int len = nativeElements.size(); 223 Record rec = null; 224 for (; ((rec = nativeElements.get(i)) == null) && (i < len); ++i); 225 records.add(rec); 226 } 227 228 /** 229 * Called by native code. 230 */ 231 private void addSymbolRecord(int i, String lookupString)232 addSymbolRecord(int i, String lookupString) 233 { 234 int len = nativeElements.size(); 235 Record rec = null; 236 for (; ((rec = nativeElements.get(i)) == null) && (i < len); ++i); 237 SymbolValueRecord symbol = SymbolValueRecord.class.cast(rec); 238 if (symbol instanceof KernelSymbolRecord) { 239 KernelSymbolRecord.class.cast(symbol).setSymbol(lookupString); 240 } else if (symbol instanceof UserSymbolRecord) { 241 UserSymbolRecord.class.cast(symbol).setSymbol(lookupString); 242 } else { 243 throw new IllegalStateException("no symbol record at index " + i); 244 } 245 records.add(symbol); 246 } 247 248 /** 249 * Called by native code. 250 */ 251 private void addStackRecord(int i, String framesString)252 addStackRecord(int i, String framesString) 253 { 254 int len = nativeElements.size(); 255 Record rec = null; 256 for (; ((rec = nativeElements.get(i)) == null) && (i < len); ++i); 257 StackValueRecord stack = StackValueRecord.class.cast(rec); 258 StackFrame[] frames = KernelStackRecord.parse(framesString); 259 if (stack instanceof KernelStackRecord) { 260 KernelStackRecord.class.cast(stack).setStackFrames(frames); 261 } else if (stack instanceof UserStackRecord) { 262 UserStackRecord.class.cast(stack).setStackFrames(frames); 263 } else { 264 throw new IllegalStateException("no stack record at index " + i); 265 } 266 records.add(stack); 267 } 268 269 /** 270 * Called by native code. 271 */ 272 private void addPrintfRecord()273 addPrintfRecord() 274 { 275 records.add(new PrintfRecord()); 276 } 277 278 /** 279 * Called by native code. 280 */ 281 private void addPrintaRecord(long snaptimeNanos, boolean isFormatString)282 addPrintaRecord(long snaptimeNanos, boolean isFormatString) 283 { 284 records.add(new PrintaRecord(snaptimeNanos, isFormatString)); 285 } 286 287 private PrintaRecord getLastPrinta()288 getLastPrinta() 289 { 290 ListIterator <Record> itr = records.listIterator(records.size()); 291 PrintaRecord printa = null; 292 Record record; 293 while (itr.hasPrevious() && (printa == null)) { 294 record = itr.previous(); 295 if (record instanceof PrintaRecord) { 296 printa = PrintaRecord.class.cast(record); 297 } 298 } 299 return printa; 300 } 301 302 /** 303 * Called by native code. 304 */ 305 private void addAggregationRecord(String aggregationName, long aggid, AggregationRecord rec)306 addAggregationRecord(String aggregationName, long aggid, 307 AggregationRecord rec) 308 { 309 PrintaRecord printa = getLastPrinta(); 310 if (printa == null) { 311 throw new IllegalStateException( 312 "No PrintaRecord in this ProbeData"); 313 } 314 printa.addRecord(aggregationName, aggid, rec); 315 } 316 317 /** 318 * Called by native code. 319 */ 320 private void invalidatePrintaRecord()321 invalidatePrintaRecord() 322 { 323 PrintaRecord printa = getLastPrinta(); 324 if (printa == null) { 325 throw new IllegalStateException( 326 "No PrintaRecord in this ProbeData"); 327 } 328 printa.invalidate(); 329 } 330 331 /** 332 * Called by native code. 333 */ 334 private void addPrintaFormattedString(Tuple tuple, String s)335 addPrintaFormattedString(Tuple tuple, String s) 336 { 337 PrintaRecord printa = getLastPrinta(); 338 if (printa == null) { 339 throw new IllegalStateException( 340 "No PrintaRecord in this ProbeData"); 341 } 342 printa.addFormattedString(tuple, s); 343 } 344 345 /** 346 * Called by native code. 347 */ 348 private void addExitRecord(int i)349 addExitRecord(int i) 350 { 351 int len = nativeElements.size(); 352 Record rec = null; 353 for (; ((rec = nativeElements.get(i)) == null) && (i < len); ++i); 354 ScalarRecord scalar = ScalarRecord.class.cast(rec); 355 Integer exitStatus = Integer.class.cast(scalar.getValue()); 356 records.add(new ExitRecord(exitStatus)); 357 } 358 359 /** 360 * Called by native code. Attaches native probedata elements cached 361 * between the given first index and last index inclusive to the most 362 * recently added record if applicable. 363 */ 364 private void attachRecordElements(int first, int last)365 attachRecordElements(int first, int last) 366 { 367 Record record = records.get(records.size() - 1); 368 if (record instanceof PrintfRecord) { 369 PrintfRecord printf = PrintfRecord.class.cast(record); 370 Record e; 371 for (int i = first; i <= last; ++i) { 372 e = nativeElements.get(i); 373 if (e == null) { 374 // printf() unformatted elements are preceded by one 375 // null for every D program statement preceding the 376 // printf() that is not a D action, such as 377 // assignment to a variable (generates a probedata 378 // record with no data). 379 continue; 380 } 381 printf.addUnformattedElement(ScalarRecord.class.cast(e)); 382 } 383 } 384 } 385 386 /** 387 * Called by native code. 388 */ 389 void clearNativeElements()390 clearNativeElements() 391 { 392 nativeElements = null; 393 } 394 395 /** 396 * Called by native code. 397 */ 398 private void setFormattedString(String s)399 setFormattedString(String s) 400 { 401 Record record = records.get(records.size() - 1); 402 if (record instanceof PrintfRecord) { 403 PrintfRecord printf = PrintfRecord.class.cast(record); 404 printf.setFormattedString(s); 405 } 406 } 407 408 /** 409 * Convenience method, gets a comparator that sorts multiple {@link 410 * ProbeDescription} instances by the specified field or fields. If 411 * more than one sort field is specified, the probe data are sorted 412 * by the first field, and in case of a tie, by the second field, 413 * and so on, in the order that the fields are specified. 414 * 415 * @param f field specifiers given in descending order of sort 416 * priority; lower priority fields are only compared (as a tie 417 * breaker) when all higher priority fields are equal 418 * @return non-null probe data comparator that sorts by the 419 * specified sort fields in the given order 420 */ 421 public static Comparator <ProbeData> getComparator(KeyField .... f)422 getComparator(KeyField ... f) 423 { 424 return new Cmp(f); 425 } 426 427 private static class Cmp implements Comparator <ProbeData> { 428 private KeyField[] sortFields; 429 430 private Cmp(KeyField .... f)431 Cmp(KeyField ... f) 432 { 433 sortFields = f; 434 } 435 436 public int compare(ProbeData d1, ProbeData d2)437 compare(ProbeData d1, ProbeData d2) 438 { 439 return ProbeData.compare(d1, d2, sortFields); 440 } 441 } 442 443 static int compareUnsigned(int i1, int i2)444 compareUnsigned(int i1, int i2) 445 { 446 int cmp; 447 448 if (i1 < 0) { 449 if (i2 < 0) { 450 cmp = (i1 < i2 ? -1 : (i1 > i2 ? 1 : 0)); 451 } else { 452 cmp = 1; // negative > positive 453 } 454 } else if (i2 < 0) { 455 cmp = -1; // positive < negative 456 } else { 457 cmp = (i1 < i2 ? -1 : (i1 > i2 ? 1 : 0)); 458 } 459 460 return cmp; 461 } 462 463 static int compareUnsigned(long i1, long i2)464 compareUnsigned(long i1, long i2) 465 { 466 int cmp; 467 468 if (i1 < 0) { 469 if (i2 < 0) { 470 cmp = (i1 < i2 ? -1 : (i1 > i2 ? 1 : 0)); 471 } else { 472 cmp = 1; // negative > positive 473 } 474 } else if (i2 < 0) { 475 cmp = -1; // positive < negative 476 } else { 477 cmp = (i1 < i2 ? -1 : (i1 > i2 ? 1 : 0)); 478 } 479 480 return cmp; 481 } 482 483 static int compareUnsigned(byte i1, byte i2)484 compareUnsigned(byte i1, byte i2) 485 { 486 int cmp; 487 488 if (i1 < 0) { 489 if (i2 < 0) { 490 cmp = (i1 < i2 ? -1 : (i1 > i2 ? 1 : 0)); 491 } else { 492 cmp = 1; // negative > positive 493 } 494 } else if (i2 < 0) { 495 cmp = -1; // positive < negative 496 } else { 497 cmp = (i1 < i2 ? -1 : (i1 > i2 ? 1 : 0)); 498 } 499 500 return cmp; 501 } 502 503 static int compareByteArrays(byte[] a1, byte[] a2)504 compareByteArrays(byte[] a1, byte[] a2) 505 { 506 int cmp = 0; 507 int len1 = a1.length; 508 int len2 = a2.length; 509 510 for (int i = 0; (cmp == 0) && (i < len1) && (i < len2); ++i) { 511 cmp = compareUnsigned(a1[i], a2[i]); 512 } 513 514 if (cmp == 0) { 515 cmp = (len1 < len2 ? -1 : (len1 > len2 ? 1 : 0)); 516 } 517 518 return cmp; 519 } 520 521 @SuppressWarnings("unchecked") 522 static int compareUnsigned(Comparable v1, Comparable v2)523 compareUnsigned(Comparable v1, Comparable v2) 524 { 525 int cmp; 526 527 if (v1 instanceof Integer) { 528 int i1 = Integer.class.cast(v1); 529 int i2 = Integer.class.cast(v2); 530 cmp = compareUnsigned(i1, i2); 531 } else if (v1 instanceof Long) { 532 long i1 = Long.class.cast(v1); 533 long i2 = Long.class.cast(v2); 534 cmp = compareUnsigned(i1, i2); 535 } else { 536 cmp = v1.compareTo(v2); 537 } 538 539 return cmp; 540 } 541 542 /** 543 * @throws ClassCastException if records or their data are not 544 * mutually comparable 545 */ 546 @SuppressWarnings("unchecked") 547 private static int compareRecords(Record r1, Record r2)548 compareRecords(Record r1, Record r2) 549 { 550 int cmp; 551 if (r1 instanceof ScalarRecord) { 552 ScalarRecord t1 = ScalarRecord.class.cast(r1); 553 ScalarRecord t2 = ScalarRecord.class.cast(r2); 554 Object o1 = t1.getValue(); 555 Object o2 = t2.getValue(); 556 if (o1 instanceof byte[]) { 557 byte[] a1 = byte[].class.cast(o1); 558 byte[] a2 = byte[].class.cast(o2); 559 cmp = compareByteArrays(a1, a2); 560 } else { 561 Comparable v1 = Comparable.class.cast(o1); 562 Comparable v2 = Comparable.class.cast(o2); 563 cmp = v1.compareTo(v2); // compare signed values 564 } 565 } else if (r1 instanceof Comparable) { 566 // StackValueRecord, SymbolValueRecord 567 Comparable v1 = Comparable.class.cast(r1); 568 Comparable v2 = Comparable.class.cast(r2); 569 cmp = v1.compareTo(v2); 570 } else if (r1 instanceof ExitRecord) { 571 ExitRecord e1 = ExitRecord.class.cast(r1); 572 ExitRecord e2 = ExitRecord.class.cast(r2); 573 int status1 = e1.getStatus(); 574 int status2 = e2.getStatus(); 575 cmp = (status1 < status2 ? -1 : (status1 > status2 ? 1 : 0)); 576 } else { 577 // PrintfRecord, PrintaRecord 578 r1.getClass().cast(r2); 579 String s1 = r1.toString(); 580 String s2 = r2.toString(); 581 cmp = s1.compareTo(s2); 582 } 583 584 return cmp; 585 } 586 587 /** 588 * @throws ClassCastException if lists are not mutually comparable 589 * because corresponding list elements are not comparable or the 590 * list themselves are different lengths 591 */ 592 private static int compareRecordLists(ProbeData d1, ProbeData d2)593 compareRecordLists(ProbeData d1, ProbeData d2) 594 { 595 List <Record> list1 = d1.getRecords(); 596 List <Record> list2 = d2.getRecords(); 597 int len1 = list1.size(); 598 int len2 = list2.size(); 599 if (len1 != len2) { 600 throw new ClassCastException("Record lists of different " + 601 "length are not comparable (lengths are " + 602 len1 + " and " + len2 + ")."); 603 } 604 605 int cmp; 606 Record r1; 607 Record r2; 608 609 for (int i = 0; (i < len1) && (i < len2); ++i) { 610 r1 = list1.get(i); 611 r2 = list2.get(i); 612 613 cmp = compareRecords(r1, r2); 614 if (cmp != 0) { 615 return cmp; 616 } 617 } 618 619 return 0; 620 } 621 622 private static int compare(ProbeData d1, ProbeData d2, KeyField[] comparedFields)623 compare(ProbeData d1, ProbeData d2, KeyField[] comparedFields) 624 { 625 int cmp; 626 for (KeyField f : comparedFields) { 627 switch (f) { 628 case CPU: 629 int cpu1 = d1.getCPU(); 630 int cpu2 = d2.getCPU(); 631 cmp = (cpu1 < cpu2 ? -1 : (cpu1 > cpu2 ? 1 : 0)); 632 break; 633 case PROBE: 634 ProbeDescription p1 = d1.getEnabledProbeDescription(); 635 ProbeDescription p2 = d2.getEnabledProbeDescription(); 636 cmp = p1.compareTo(p2); 637 break; 638 case EPID: 639 int epid1 = d1.getEnabledProbeID(); 640 int epid2 = d2.getEnabledProbeID(); 641 cmp = (epid1 < epid2 ? -1 : (epid1 > epid2 ? 1 : 0)); 642 break; 643 case RECORDS: 644 cmp = compareRecordLists(d1, d2); 645 break; 646 default: 647 throw new IllegalArgumentException( 648 "Unexpected sort field " + f); 649 } 650 651 if (cmp != 0) { 652 return cmp; 653 } 654 } 655 656 return 0; 657 } 658 659 /** 660 * Gets the enabled probe ID. Identifies the enabled probe that 661 * fired and generated this {@code ProbeData}. The "epid" is 662 * different from {@link ProbeDescription#getID()} in that it 663 * identifies a probe among all probes enabled by the source {@link 664 * Consumer}, rather than among all the probes on the system. 665 * 666 * @return the enabled probe ID generated by the native DTrace 667 * library 668 */ 669 public int getEnabledProbeID()670 getEnabledProbeID() 671 { 672 return epid; 673 } 674 675 /** 676 * Gets the ID of the CPU on which the probe fired. 677 * 678 * @return ID of the CPU on which the probe fired 679 */ 680 public int getCPU()681 getCPU() 682 { 683 return cpu; 684 } 685 686 /** 687 * Gets the enabled probe description. Identifies the enabled probe 688 * that fired and generated this {@code ProbeData}. 689 * 690 * @return non-null probe description 691 */ 692 public ProbeDescription getEnabledProbeDescription()693 getEnabledProbeDescription() 694 { 695 return enabledProbeDescription; 696 } 697 698 /** 699 * Gets the current state of control flow (function entry or return, 700 * and depth in call stack) at the time of the probe firing that 701 * generated this {@code ProbeData} instance, or {@code null} if 702 * such information was not requested with the {@code flowindent} 703 * option. 704 * 705 * @return a description of control flow across function boundaries, 706 * or {@code null} if {@code Consumer.getOption(Option.flowindent)} 707 * returns {@link Option#UNSET} 708 * @see Consumer#setOption(String option) 709 * @see Option#flowindent 710 */ 711 public Flow getFlow()712 getFlow() 713 { 714 return flow; 715 } 716 717 /** 718 * Gets the records generated by the actions of the probe that 719 * fired, in the same order as the actions that generated the 720 * records. The returned list includes one record for every 721 * record-generating D action (some D actions, such as {@code 722 * clear()}, do not generate records). 723 * 724 * @return non-null, unmodifiable list view of the records belonging 725 * to this {@code ProbeData} in the order of the actions in the 726 * DTrace probe that generated them (record-producing actions are 727 * generally those that produce output, such as {@code printf()}, 728 * but also the {@code exit()} action) 729 */ 730 public List <Record> getRecords()731 getRecords() 732 { 733 return Collections. <Record> unmodifiableList(records); 734 } 735 736 /** 737 * Natural ordering of probe data. Sorts probe data by records 738 * first, then if record data is equal, by enabled probe ID. 739 * 740 * @param d probe data to be compared with this probe data 741 * @return a negative number, zero, or a positive number as this 742 * probe data is less than, equal to, or greater than the given 743 * probe data 744 * @see ProbeData#getComparator(KeyField[] f) 745 * @throws NullPointerException if the given probe data is 746 * {@code null} 747 * @throws ClassCastException if record lists of both {@code 748 * ProbeData} instances are not mutually comparable because 749 * corresponding list elements are not comparable or the lists 750 * themselves are different lengths 751 */ 752 public int compareTo(ProbeData d)753 compareTo(ProbeData d) 754 { 755 return DEFAULT_CMP.compare(this, d); 756 } 757 758 private void readObject(ObjectInputStream s)759 readObject(ObjectInputStream s) 760 throws IOException, ClassNotFoundException 761 { 762 s.defaultReadObject(); 763 // Defensively copy record list _before_ validating. 764 int len = records.size(); 765 ArrayList <Record> copy = new ArrayList <Record> (len); 766 copy.addAll(records); 767 records = copy; 768 // Check class invariants 769 try { 770 validate(); 771 } catch (Exception e) { 772 InvalidObjectException x = new InvalidObjectException( 773 e.getMessage()); 774 x.initCause(e); 775 throw x; 776 } 777 } 778 779 /** 780 * Gets a string representation of this {@code ProbeData} instance 781 * useful for logging and not intended for display. The exact 782 * details of the representation are unspecified and subject to 783 * change, but the following format may be regarded as typical: 784 * <pre><code> 785 * class-name[property1 = value1, property2 = value2] 786 * </code></pre> 787 */ 788 public String toString()789 toString() 790 { 791 StringBuilder buf = new StringBuilder(); 792 buf.append(ProbeData.class.getName()); 793 buf.append("[epid = "); 794 buf.append(epid); 795 buf.append(", cpu = "); 796 buf.append(cpu); 797 buf.append(", enabledProbeDescription = "); 798 buf.append(enabledProbeDescription); 799 buf.append(", flow = "); 800 buf.append(flow); 801 buf.append(", records = "); 802 803 Record record; 804 Object value; 805 buf.append('['); 806 for (int i = 0; i < records.size(); ++i) { 807 if (i > 0) { 808 buf.append(", "); 809 } 810 record = records.get(i); 811 if (record instanceof ValueRecord) { 812 value = ValueRecord.class.cast(record).getValue(); 813 if (value instanceof String) { 814 buf.append("\""); 815 buf.append(String.class.cast(value)); 816 buf.append("\""); 817 } else { 818 buf.append(record); 819 } 820 } else { 821 buf.append(record); 822 } 823 } 824 buf.append(']'); 825 826 buf.append(']'); 827 return buf.toString(); 828 } 829 } 830