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 2007 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.io.*; 31 import java.beans.*; 32 33 /** 34 * State of a target process designated by {@link 35 * Consumer#createProcess(String command)} or {@link 36 * Consumer#grabProcess(int pid)}. 37 * <p> 38 * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. 39 * 40 * @see ConsumerListener#processStateChanged(ProcessEvent e) 41 * 42 * @author Tom Erickson 43 */ 44 public final class ProcessState implements Serializable { 45 static final long serialVersionUID = -3395911213431317292L; 46 47 static { 48 try { 49 BeanInfo info = Introspector.getBeanInfo(ProcessState.class); 50 PersistenceDelegate persistenceDelegate = 51 new DefaultPersistenceDelegate( 52 new String[] {"processID", "state", 53 "terminationSignal", "terminationSignalName", 54 "exitStatus", "message"}) 55 { 56 /* 57 * Need to prevent DefaultPersistenceDelegate from using 58 * overridden equals() method, resulting in a 59 * StackOverFlowError. Revert to PersistenceDelegate 60 * implementation. See 61 * http://forum.java.sun.com/thread.jspa?threadID= 62 * 477019&tstart=135 63 */ 64 protected boolean 65 mutatesTo(Object oldInstance, Object newInstance) 66 { 67 return (newInstance != null && oldInstance != null && 68 oldInstance.getClass() == newInstance.getClass()); 69 } 70 71 protected Expression 72 instantiate(Object oldInstance, Encoder out) 73 { 74 ProcessState pstate = (ProcessState)oldInstance; 75 return new Expression(oldInstance, oldInstance.getClass(), 76 "new", new Object[] { pstate.getProcessID(), 77 pstate.getState().name(), 78 pstate.getTerminationSignal(), 79 pstate.getTerminationSignalName(), 80 pstate.getExitStatus(), 81 pstate.getMessage() }); 82 } 83 }; 84 BeanDescriptor d = info.getBeanDescriptor(); 85 d.setValue("persistenceDelegate", persistenceDelegate); 86 } catch (IntrospectionException e) { 87 System.out.println(e); 88 } 89 } 90 91 /** 92 * State of a target process. 93 */ 94 public enum State { 95 /** Process is running. */ 96 RUN, 97 /** Process is stopped. */ 98 STOP, 99 /** Process is lost to control. */ 100 LOST, 101 /** Process is terminated (zombie). */ 102 UNDEAD, 103 /** Process is terminated (core file). */ 104 DEAD 105 } 106 107 /** @serial */ 108 private int processID; 109 /** @serial */ 110 private State state; 111 /** @serial */ 112 private int terminationSignal; 113 /** @serial */ 114 private String terminationSignalName; 115 /** @serial */ 116 private Integer exitStatus; 117 /** @serial */ 118 private String message; 119 120 /** 121 * Creates a {@code ProcessState} instance with the given state. 122 * 123 * @param pid non-negative target process ID 124 * @param processState target process state 125 * @param processTerminationSignal signal that terminated the target 126 * process, {@code -1} if the process was not terminated by a signal 127 * or if the terminating signal is unknown 128 * @param processTerminationSignalName name of the signal that 129 * terminated the target process, {@code null} if the process was 130 * not terminated by a signal or if the terminating signal is 131 * unknown 132 * @param processExitStatus target process exit status, {@code null} 133 * if the process has not exited or the exit status is unknown 134 * @param msg message included by DTrace, if any 135 * @throws NullPointerException if the given process state is {@code 136 * null} 137 * @throws IllegalArgumentException if the given process ID is negative 138 */ 139 public ProcessState(int pid, State processState, int processTerminationSignal, String processTerminationSignalName, Integer processExitStatus, String msg)140 ProcessState(int pid, State processState, 141 int processTerminationSignal, 142 String processTerminationSignalName, 143 Integer processExitStatus, String msg) 144 { 145 processID = pid; 146 state = processState; 147 terminationSignal = processTerminationSignal; 148 terminationSignalName = processTerminationSignalName; 149 exitStatus = processExitStatus; 150 message = msg; 151 validate(); 152 } 153 154 /** 155 * Supports XML persistence. 156 * 157 * @see #ProcessState(int pid, State processState, int 158 * processTerminationSignal, String processTerminationSignalName, 159 * Integer processExitStatus, String msg) 160 * @throws IllegalArgumentException if there is no {@link 161 * ProcessState.State} value with the given state name. 162 */ 163 public ProcessState(int pid, String processStateName, int processTerminationSignal, String processTerminationSignalName, Integer processExitStatus, String msg)164 ProcessState(int pid, String processStateName, 165 int processTerminationSignal, 166 String processTerminationSignalName, 167 Integer processExitStatus, String msg) 168 { 169 processID = pid; 170 state = Enum.valueOf(State.class, processStateName); 171 terminationSignal = processTerminationSignal; 172 terminationSignalName = processTerminationSignalName; 173 exitStatus = processExitStatus; 174 message = msg; 175 validate(); 176 } 177 178 private final void validate()179 validate() 180 { 181 if (processID < 0) { 182 throw new IllegalArgumentException("pid is negative"); 183 } 184 if (state == null) { 185 throw new NullPointerException("process state is null"); 186 } 187 } 188 189 /** 190 * Gets the process ID. 191 * 192 * @return non-negative target process ID 193 */ 194 public int getProcessID()195 getProcessID() 196 { 197 return processID; 198 } 199 200 /** 201 * Gets the process state. 202 * 203 * @return non-null target process state 204 */ 205 public State getState()206 getState() 207 { 208 return state; 209 } 210 211 /** 212 * Gets the signal that terminated the process. 213 * 214 * @return termination signal, {@code -1} if the process was not 215 * terminated by a signal or if the terminating signal is unknown 216 */ 217 public int getTerminationSignal()218 getTerminationSignal() 219 { 220 return terminationSignal; 221 } 222 223 /** 224 * Gets the name of the signal that terminated the process. 225 * 226 * @return termination signal name, {@code null} if the process was 227 * not terminated by a signal or if the terminating signal is 228 * unknown 229 */ 230 public String getTerminationSignalName()231 getTerminationSignalName() 232 { 233 return terminationSignalName; 234 } 235 236 /** 237 * Gets the process exit status. 238 * 239 * @return exit status, or {@code null} if the process has not 240 * exited or the exit status is unknown 241 */ 242 public Integer getExitStatus()243 getExitStatus() 244 { 245 return exitStatus; 246 } 247 248 /** 249 * Called by native code. 250 */ 251 private void setExitStatus(int status)252 setExitStatus(int status) 253 { 254 exitStatus = new Integer(status); 255 } 256 257 /** 258 * Gets the message from DTrace describing this process state. 259 * 260 * @return DTrace message, or {@code null} if DTrace did not include 261 * a message with this process state 262 */ 263 public String getMessage()264 getMessage() 265 { 266 return message; 267 } 268 269 /** 270 * Compares the specified object with this {@code ProcessState} 271 * instance for equality. Defines equality as having the same 272 * attributes. 273 * 274 * @return {@code true} if and only if the specified object is also 275 * a {@code ProcessState} and both instances have the same 276 * attributes 277 */ 278 @Override 279 public boolean equals(Object o)280 equals(Object o) 281 { 282 if (o instanceof ProcessState) { 283 ProcessState s = (ProcessState)o; 284 return ((processID == s.processID) && 285 (state == s.state) && 286 (terminationSignal == s.terminationSignal) && 287 ((terminationSignalName == null) ? 288 (s.terminationSignalName == null) : 289 terminationSignalName.equals(s.terminationSignalName)) && 290 ((exitStatus == null) ? 291 (s.exitStatus == null) : 292 exitStatus.equals(s.exitStatus)) && 293 ((message == null) ? (s.message == null) : 294 message.equals(s.message))); 295 } 296 return false; 297 } 298 299 /** 300 * Overridden to ensure that equal instances have equal hash codes. 301 */ 302 @Override 303 public int hashCode()304 hashCode() 305 { 306 int hash = 17; 307 hash = (37 * hash) + processID; 308 hash = (37 * hash) + state.hashCode(); 309 hash = (37 * hash) + terminationSignal; 310 hash = (37 * hash) + (exitStatus == null ? 0 : 311 exitStatus.hashCode()); 312 hash = (37 * hash) + (message == null ? 0 : message.hashCode()); 313 return hash; 314 } 315 316 private void readObject(ObjectInputStream s)317 readObject(ObjectInputStream s) 318 throws IOException, ClassNotFoundException 319 { 320 s.defaultReadObject(); 321 // check class invariants 322 try { 323 validate(); 324 } catch (Exception e) { 325 InvalidObjectException x = new InvalidObjectException( 326 e.getMessage()); 327 x.initCause(e); 328 throw x; 329 } 330 } 331 332 /** 333 * Gets a string representation of this process state useful for 334 * logging and not intended for display. The exact details of the 335 * representation are unspecified and subject to change, but the 336 * following format may be regarded as typical: 337 * <pre><code> 338 * class-name[property1 = value1, property2 = value2] 339 * </code></pre> 340 */ 341 public String toString()342 toString() 343 { 344 StringBuilder buf = new StringBuilder(); 345 buf.append(ProcessState.class.getName()); 346 buf.append("[pid = "); 347 buf.append(processID); 348 buf.append(", state = "); 349 buf.append(state); 350 buf.append(", terminationSignal = "); 351 buf.append(terminationSignal); 352 buf.append(", terminationSignalName = "); 353 buf.append(terminationSignalName); 354 buf.append(", exitStatus = "); 355 buf.append(exitStatus); 356 buf.append(", message = "); 357 buf.append(message); 358 buf.append(']'); 359 return buf.toString(); 360 } 361 } 362