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