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 /* 23*e77b06d2Stomee * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24fb3fb4f3Stomee * Use is subject to license terms. 25fb3fb4f3Stomee * 26fb3fb4f3Stomee * ident "%Z%%M% %I% %E% SMI" 27fb3fb4f3Stomee */ 28fb3fb4f3Stomee package org.opensolaris.os.dtrace; 29fb3fb4f3Stomee 30fb3fb4f3Stomee import java.util.*; 31fb3fb4f3Stomee import java.io.*; 32fb3fb4f3Stomee import java.util.regex.Pattern; 33fb3fb4f3Stomee import java.beans.*; 34fb3fb4f3Stomee 35fb3fb4f3Stomee /** 36fb3fb4f3Stomee * A value generated by the DTrace {@code stack()} action. 37fb3fb4f3Stomee * <p> 38fb3fb4f3Stomee * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. 39fb3fb4f3Stomee * 40fb3fb4f3Stomee * @author Tom Erickson 41fb3fb4f3Stomee */ 42fb3fb4f3Stomee public final class KernelStackRecord implements StackValueRecord, 43fb3fb4f3Stomee Serializable, Comparable <KernelStackRecord> 44fb3fb4f3Stomee { 45fb3fb4f3Stomee static final long serialVersionUID = 8616454544771346573L; 46fb3fb4f3Stomee static final int STACK_INDENT = 14; 47fb3fb4f3Stomee static final StackFrame[] EMPTY_FRAMES = new StackFrame[] {}; 48fb3fb4f3Stomee 49fb3fb4f3Stomee static { 50fb3fb4f3Stomee try { 51fb3fb4f3Stomee BeanInfo info = Introspector.getBeanInfo(KernelStackRecord.class); 52fb3fb4f3Stomee PersistenceDelegate persistenceDelegate = 53fb3fb4f3Stomee new DefaultPersistenceDelegate( 54fb3fb4f3Stomee new String[] {"stackFrames", "rawStackData"}) 55fb3fb4f3Stomee { 56fb3fb4f3Stomee /* 57fb3fb4f3Stomee * Need to prevent DefaultPersistenceDelegate from using 58fb3fb4f3Stomee * overridden equals() method, resulting in a 59fb3fb4f3Stomee * StackOverFlowError. Revert to PersistenceDelegate 60fb3fb4f3Stomee * implementation. See 61fb3fb4f3Stomee * http://forum.java.sun.com/thread.jspa?threadID= 62fb3fb4f3Stomee * 477019&tstart=135 63fb3fb4f3Stomee */ 64fb3fb4f3Stomee protected boolean 65fb3fb4f3Stomee mutatesTo(Object oldInstance, Object newInstance) 66fb3fb4f3Stomee { 67fb3fb4f3Stomee return (newInstance != null && oldInstance != null && 68fb3fb4f3Stomee oldInstance.getClass() == newInstance.getClass()); 69fb3fb4f3Stomee } 70fb3fb4f3Stomee }; 71fb3fb4f3Stomee BeanDescriptor d = info.getBeanDescriptor(); 72fb3fb4f3Stomee d.setValue("persistenceDelegate", persistenceDelegate); 73fb3fb4f3Stomee } catch (IntrospectionException e) { 74fb3fb4f3Stomee System.out.println(e); 75fb3fb4f3Stomee } 76fb3fb4f3Stomee } 77fb3fb4f3Stomee 78fb3fb4f3Stomee /** 79fb3fb4f3Stomee * Splits formatted call stack generated by DTrace stack() and 80fb3fb4f3Stomee * ustack() actions into tokens delimited by whitespace. Matches 81fb3fb4f3Stomee * any number of whitespace characters on either side of a newline. 82fb3fb4f3Stomee * Can't assume that a line has no whitespace characters. A java 83fb3fb4f3Stomee * stack might have the line "StubRoutines (1)", which must not get 84fb3fb4f3Stomee * split into two tokens. 85fb3fb4f3Stomee */ 86fb3fb4f3Stomee static final Pattern STACK_TOKENIZER = Pattern.compile("\\s*\n\\s*"); 87fb3fb4f3Stomee 88fb3fb4f3Stomee /** 89fb3fb4f3Stomee * Called by JNI layer to convert a stack formatted by the native 90fb3fb4f3Stomee * DTrace library into an unformatted array of stack frames. 91fb3fb4f3Stomee * 92fb3fb4f3Stomee * @param s string representation of stack data generated by the D 93fb3fb4f3Stomee * {@code stack()}, {@code ustack()}, or {@code jstack()} action 94fb3fb4f3Stomee * @return array of human-readable stack frames 95fb3fb4f3Stomee */ 96fb3fb4f3Stomee static StackFrame[] parse(String s)97fb3fb4f3Stomee parse(String s) 98fb3fb4f3Stomee { 99fb3fb4f3Stomee // 100fb3fb4f3Stomee // First trim the leading whitespace to avoid an initial empty 101fb3fb4f3Stomee // element in the returned array. 102fb3fb4f3Stomee // 103fb3fb4f3Stomee s = s.trim(); 104fb3fb4f3Stomee StackFrame[] frames; 105fb3fb4f3Stomee if (s.length() == 0) { 106fb3fb4f3Stomee frames = EMPTY_FRAMES; 107fb3fb4f3Stomee } else { 108fb3fb4f3Stomee String[] f = STACK_TOKENIZER.split(s); 109fb3fb4f3Stomee int n = f.length; 110fb3fb4f3Stomee frames = new StackFrame[n]; 111fb3fb4f3Stomee for (int i = 0; i < n; ++i) { 112fb3fb4f3Stomee frames[i] = new StackFrame(f[i]); 113fb3fb4f3Stomee } 114fb3fb4f3Stomee } 115fb3fb4f3Stomee return frames; 116fb3fb4f3Stomee } 117fb3fb4f3Stomee 118fb3fb4f3Stomee /** @serial */ 119fb3fb4f3Stomee private StackFrame[] stackFrames; 120fb3fb4f3Stomee /** @serial */ 121fb3fb4f3Stomee private byte[] rawStackData; 122fb3fb4f3Stomee 123fb3fb4f3Stomee /** 124fb3fb4f3Stomee * Called by native code and by UserStackRecord (in its constructor 125fb3fb4f3Stomee * called by native code). 126fb3fb4f3Stomee * 127fb3fb4f3Stomee * @throws NullPointerException if rawBytes is {@code null} 128fb3fb4f3Stomee */ KernelStackRecord(byte[] rawBytes)129fb3fb4f3Stomee KernelStackRecord(byte[] rawBytes) 130fb3fb4f3Stomee { 131fb3fb4f3Stomee // No need for defensive copy; native code will not modify input 132fb3fb4f3Stomee // raw bytes. 133fb3fb4f3Stomee rawStackData = rawBytes; 134fb3fb4f3Stomee if (rawStackData == null) { 135fb3fb4f3Stomee throw new NullPointerException("raw stack data is null"); 136fb3fb4f3Stomee } 137fb3fb4f3Stomee } 138fb3fb4f3Stomee 139fb3fb4f3Stomee /** 140fb3fb4f3Stomee * Creates a {@code KernelStackRecord} with the given stack frames 141fb3fb4f3Stomee * and raw stack data. Supports XML persistence. 142fb3fb4f3Stomee * 143fb3fb4f3Stomee * @param frames array of human-readable stack frames, copied so 144fb3fb4f3Stomee * that later modifying the given frames array will not affect this 145fb3fb4f3Stomee * {@code KernelStackRecord}; may be {@code null} or empty to 146fb3fb4f3Stomee * indicate that the raw stack data was not converted to 147fb3fb4f3Stomee * human-readable stack frames (see {@link 148fb3fb4f3Stomee * StackValueRecord#getStackFrames()}) 149fb3fb4f3Stomee * @param rawBytes array of raw bytes used to represent this stack 150fb3fb4f3Stomee * value in the native DTrace library, needed to distinguish stacks 151fb3fb4f3Stomee * that have the same display value but are considered distinct by 152fb3fb4f3Stomee * DTrace; copied so that later modifying the given array will not 153fb3fb4f3Stomee * affect this {@code KernelStackRecord} 154fb3fb4f3Stomee * @throws NullPointerException if the given array of raw bytes is 155fb3fb4f3Stomee * {@code null} or if any element of the {@code frames} array is 156fb3fb4f3Stomee * {@code null} 157fb3fb4f3Stomee */ 158fb3fb4f3Stomee public KernelStackRecord(StackFrame[] frames, byte[] rawBytes)159fb3fb4f3Stomee KernelStackRecord(StackFrame[] frames, byte[] rawBytes) 160fb3fb4f3Stomee { 161fb3fb4f3Stomee if (frames != null) { 162*e77b06d2Stomee stackFrames = frames.clone(); 163fb3fb4f3Stomee } 164fb3fb4f3Stomee if (rawBytes != null) { 165*e77b06d2Stomee rawStackData = rawBytes.clone(); 166fb3fb4f3Stomee } 167fb3fb4f3Stomee validate(); 168fb3fb4f3Stomee } 169fb3fb4f3Stomee 17091cfa10aStomee private final void validate()171fb3fb4f3Stomee validate() 172fb3fb4f3Stomee { 173fb3fb4f3Stomee if (rawStackData == null) { 174fb3fb4f3Stomee throw new NullPointerException("raw stack data is null"); 175fb3fb4f3Stomee } 176fb3fb4f3Stomee // stackFrames may be null; if non-null, cannot contain null 177fb3fb4f3Stomee // elements 178fb3fb4f3Stomee if (stackFrames != null) { 179fb3fb4f3Stomee for (StackFrame f : stackFrames) { 180fb3fb4f3Stomee if (f == null) { 181fb3fb4f3Stomee throw new NullPointerException("stack frame is null"); 182fb3fb4f3Stomee } 183fb3fb4f3Stomee } 184fb3fb4f3Stomee } 185fb3fb4f3Stomee } 186fb3fb4f3Stomee 187fb3fb4f3Stomee public StackFrame[] getStackFrames()188fb3fb4f3Stomee getStackFrames() 189fb3fb4f3Stomee { 190fb3fb4f3Stomee if (stackFrames == null) { 191fb3fb4f3Stomee return EMPTY_FRAMES; 192fb3fb4f3Stomee } 193*e77b06d2Stomee return stackFrames.clone(); 194fb3fb4f3Stomee } 195fb3fb4f3Stomee 196fb3fb4f3Stomee /** 197fb3fb4f3Stomee * Called by native code and by UserStackRecord in its 198fb3fb4f3Stomee * setStackFrames() method. 199fb3fb4f3Stomee */ 200fb3fb4f3Stomee void setStackFrames(StackFrame[] frames)201fb3fb4f3Stomee setStackFrames(StackFrame[] frames) 202fb3fb4f3Stomee { 203fb3fb4f3Stomee // No need for defensive copy; native code will not modify input 204fb3fb4f3Stomee // frames. 205fb3fb4f3Stomee stackFrames = frames; 206fb3fb4f3Stomee validate(); 207fb3fb4f3Stomee } 208fb3fb4f3Stomee 209fb3fb4f3Stomee /** 210fb3fb4f3Stomee * Gets the native DTrace representation of this record's stack as 211fb3fb4f3Stomee * an array of raw bytes. The raw bytes are used in {@link 212fb3fb4f3Stomee * #equals(Object o) equals()} and {@link 213fb3fb4f3Stomee * #compareTo(KernelStackRecord r) compareTo()} to test equality and 214fb3fb4f3Stomee * to determine the natural ordering of kernel stack records. 215fb3fb4f3Stomee * 216fb3fb4f3Stomee * @return the native DTrace library's internal representation of 217fb3fb4f3Stomee * this record's stack as a non-null array of bytes 218fb3fb4f3Stomee */ 219fb3fb4f3Stomee public byte[] getRawStackData()220fb3fb4f3Stomee getRawStackData() 221fb3fb4f3Stomee { 222*e77b06d2Stomee return rawStackData.clone(); 223fb3fb4f3Stomee } 224fb3fb4f3Stomee 225fb3fb4f3Stomee /** 226fb3fb4f3Stomee * Gets the raw bytes used to represent this record's stack value in 227fb3fb4f3Stomee * the native DTrace library. To get a human-readable 228fb3fb4f3Stomee * representation, call {@link #toString()}. 229fb3fb4f3Stomee * 230fb3fb4f3Stomee * @return {@link #getRawStackData()} 231fb3fb4f3Stomee */ 232fb3fb4f3Stomee public Object getValue()233fb3fb4f3Stomee getValue() 234fb3fb4f3Stomee { 235*e77b06d2Stomee return rawStackData.clone(); 236fb3fb4f3Stomee } 237fb3fb4f3Stomee 238fb3fb4f3Stomee public List <StackFrame> asList()239fb3fb4f3Stomee asList() 240fb3fb4f3Stomee { 241fb3fb4f3Stomee if (stackFrames == null) { 242fb3fb4f3Stomee return Collections. <StackFrame> emptyList(); 243fb3fb4f3Stomee } 244*e77b06d2Stomee return Collections. <StackFrame> unmodifiableList( 245*e77b06d2Stomee Arrays.asList(stackFrames)); 246fb3fb4f3Stomee } 247fb3fb4f3Stomee 248fb3fb4f3Stomee /** 249fb3fb4f3Stomee * Compares the specified object with this {@code KernelStackRecord} 250fb3fb4f3Stomee * for equality. Returns {@code true} if and only if the specified 251fb3fb4f3Stomee * object is also a {@code KernelStackRecord} and both records have 252fb3fb4f3Stomee * the same raw stack data. 253fb3fb4f3Stomee * <p> 254fb3fb4f3Stomee * This implementation first checks if the specified object is this 255fb3fb4f3Stomee * {@code KernelStackRecord}. If so, it returns {@code true}. 256fb3fb4f3Stomee * 257fb3fb4f3Stomee * @return {@code true} if and only if the specified object is also 258fb3fb4f3Stomee * a {@code KernelStackRecord} and both records have the same raw 259fb3fb4f3Stomee * stack data 260fb3fb4f3Stomee */ 261fb3fb4f3Stomee @Override 262fb3fb4f3Stomee public boolean equals(Object o)263fb3fb4f3Stomee equals(Object o) 264fb3fb4f3Stomee { 265fb3fb4f3Stomee if (o == this) { 266fb3fb4f3Stomee return true; 267fb3fb4f3Stomee } 268fb3fb4f3Stomee if (o instanceof KernelStackRecord) { 269fb3fb4f3Stomee KernelStackRecord r = (KernelStackRecord)o; 270fb3fb4f3Stomee return Arrays.equals(rawStackData, r.rawStackData); 271fb3fb4f3Stomee } 272fb3fb4f3Stomee return false; 273fb3fb4f3Stomee } 274fb3fb4f3Stomee 275fb3fb4f3Stomee /** 276fb3fb4f3Stomee * Overridden to ensure that equal instances have equal hash codes. 277fb3fb4f3Stomee */ 278fb3fb4f3Stomee @Override 279fb3fb4f3Stomee public int hashCode()280fb3fb4f3Stomee hashCode() 281fb3fb4f3Stomee { 282fb3fb4f3Stomee return Arrays.hashCode(rawStackData); 283fb3fb4f3Stomee } 284fb3fb4f3Stomee 285fb3fb4f3Stomee /** 286127bbe13Stomee * Compares this record with the given {@code KernelStackRecord}. 287127bbe13Stomee * Compares the first unequal pair of bytes at the same index in 288127bbe13Stomee * each record's raw stack data, or if all corresponding bytes are 289127bbe13Stomee * equal, compares the length of each record's array of raw stack 290127bbe13Stomee * data. Corresponding bytes are compared as unsigned values. The 291127bbe13Stomee * {@code compareTo()} method is compatible with {@link 292127bbe13Stomee * #equals(Object o) equals()}. 293fb3fb4f3Stomee * <p> 294fb3fb4f3Stomee * This implementation first checks if the specified record is this 295fb3fb4f3Stomee * {@code KernelStackRecord}. If so, it returns {@code 0}. 296fb3fb4f3Stomee * 297fb3fb4f3Stomee * @return -1, 0, or 1 as this record's raw stack data is less than, 298fb3fb4f3Stomee * equal to, or greater than the given record's raw stack data. 299fb3fb4f3Stomee */ 300fb3fb4f3Stomee public int compareTo(KernelStackRecord r)301fb3fb4f3Stomee compareTo(KernelStackRecord r) 302fb3fb4f3Stomee { 303fb3fb4f3Stomee if (r == this) { 304fb3fb4f3Stomee return 0; 305fb3fb4f3Stomee } 306fb3fb4f3Stomee 307127bbe13Stomee return ProbeData.compareByteArrays(rawStackData, r.rawStackData); 308fb3fb4f3Stomee } 309fb3fb4f3Stomee 310fb3fb4f3Stomee private void readObject(ObjectInputStream s)311fb3fb4f3Stomee readObject(ObjectInputStream s) 312fb3fb4f3Stomee throws IOException, ClassNotFoundException 313fb3fb4f3Stomee { 314fb3fb4f3Stomee s.defaultReadObject(); 315fb3fb4f3Stomee // Make a defensive copy of stack frames and raw bytes 316fb3fb4f3Stomee if (stackFrames != null) { 317*e77b06d2Stomee stackFrames = stackFrames.clone(); 318fb3fb4f3Stomee } 319fb3fb4f3Stomee if (rawStackData != null) { 320*e77b06d2Stomee rawStackData = rawStackData.clone(); 321fb3fb4f3Stomee } 322fb3fb4f3Stomee // check class invariants 323fb3fb4f3Stomee try { 324fb3fb4f3Stomee validate(); 325fb3fb4f3Stomee } catch (Exception e) { 3264ae67516Stomee InvalidObjectException x = new InvalidObjectException( 3274ae67516Stomee e.getMessage()); 3284ae67516Stomee x.initCause(e); 3294ae67516Stomee throw x; 330fb3fb4f3Stomee } 331fb3fb4f3Stomee } 332fb3fb4f3Stomee 333fb3fb4f3Stomee /** 334fb3fb4f3Stomee * Gets a multi-line string representation of a stack with one frame 335fb3fb4f3Stomee * per line. Each line is of the format {@code 336fb3fb4f3Stomee * module`function+offset}, where {@code module} and {@code 337fb3fb4f3Stomee * function} are symbol names and offset is a hex integer preceded 338fb3fb4f3Stomee * by {@code 0x}. For example: {@code genunix`open+0x19}. The 339fb3fb4f3Stomee * offset (and the preceding '+' sign) are omitted if offset is 340fb3fb4f3Stomee * zero. If function name lookup fails, the raw pointer value is 341fb3fb4f3Stomee * used instead. In that case, the module name (and the ` 342fb3fb4f3Stomee * delimiter) may or may not be present, depending on whether or not 343fb3fb4f3Stomee * module lookup also fails, and a raw pointer value appears in 344fb3fb4f3Stomee * place of {@code function+offset} as a hex value preceded by 345fb3fb4f3Stomee * {@code 0x}. The format just described, not including surrounding 346fb3fb4f3Stomee * whitespce, is defined in the native DTrace library and is as 347fb3fb4f3Stomee * stable as that library definition. Each line is indented by an 348fb3fb4f3Stomee * equal (unspecified) number of spaces. 349fb3fb4f3Stomee * <p> 350fb3fb4f3Stomee * If human-readable stack frames are not available (see {@link 351fb3fb4f3Stomee * #getStackFrames()}), a table represenation of {@link 352fb3fb4f3Stomee * #getRawStackData()} is returned instead. The table displays 16 353fb3fb4f3Stomee * bytes per row in unsigned hex followed by the ASCII character 354fb3fb4f3Stomee * representations of those bytes. Each unprintable character is 355fb3fb4f3Stomee * represented by a period (.). 356fb3fb4f3Stomee */ 357fb3fb4f3Stomee @Override 358fb3fb4f3Stomee public String toString()359fb3fb4f3Stomee toString() 360fb3fb4f3Stomee { 361fb3fb4f3Stomee StackFrame[] frames = getStackFrames(); 362fb3fb4f3Stomee if (frames.length == 0) { 363fb3fb4f3Stomee return ScalarRecord.rawBytesString(rawStackData); 364fb3fb4f3Stomee } 365fb3fb4f3Stomee 3664ae67516Stomee StringBuilder buf = new StringBuilder(); 367fb3fb4f3Stomee buf.append('\n'); 368fb3fb4f3Stomee for (StackFrame f : frames) { 369fb3fb4f3Stomee for (int i = 0; i < STACK_INDENT; ++i) { 370fb3fb4f3Stomee buf.append(' '); 371fb3fb4f3Stomee } 372fb3fb4f3Stomee buf.append(f); 373fb3fb4f3Stomee buf.append('\n'); 374fb3fb4f3Stomee } 375fb3fb4f3Stomee return buf.toString(); 376fb3fb4f3Stomee } 377fb3fb4f3Stomee } 378