1127bbe13Stomee /*
2127bbe13Stomee  * CDDL HEADER START
3127bbe13Stomee  *
4127bbe13Stomee  * The contents of this file are subject to the terms of the
5127bbe13Stomee  * Common Development and Distribution License (the "License").
6127bbe13Stomee  * You may not use this file except in compliance with the License.
7127bbe13Stomee  *
8127bbe13Stomee  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9127bbe13Stomee  * or http://www.opensolaris.org/os/licensing.
10127bbe13Stomee  * See the License for the specific language governing permissions
11127bbe13Stomee  * and limitations under the License.
12127bbe13Stomee  *
13127bbe13Stomee  * When distributing Covered Code, include this CDDL HEADER in each
14127bbe13Stomee  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15127bbe13Stomee  * If applicable, add the following below this CDDL HEADER, with the
16127bbe13Stomee  * fields enclosed by brackets "[]" replaced with your own identifying
17127bbe13Stomee  * information: Portions Copyright [yyyy] [name of copyright owner]
18127bbe13Stomee  *
19127bbe13Stomee  * CDDL HEADER END
20127bbe13Stomee  */
21127bbe13Stomee 
22127bbe13Stomee /*
2391cfa10aStomee  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24127bbe13Stomee  * Use is subject to license terms.
25127bbe13Stomee  *
26127bbe13Stomee  * ident	"%Z%%M%	%I%	%E% SMI"
27127bbe13Stomee  */
28127bbe13Stomee package org.opensolaris.os.dtrace;
29127bbe13Stomee 
30127bbe13Stomee import java.io.*;
31127bbe13Stomee import java.beans.*;
32127bbe13Stomee 
33127bbe13Stomee /**
34127bbe13Stomee  * A value generated by the DTrace {@code umod()}, {@code ufunc()}, or
35127bbe13Stomee  * {@code usym()} action used to lookup the symbol associated with a
36127bbe13Stomee  * user address.
37127bbe13Stomee  * <p>
38127bbe13Stomee  * Immutable.  Supports persistence using {@link java.beans.XMLEncoder}.
39127bbe13Stomee  *
40127bbe13Stomee  * @author Tom Erickson
41127bbe13Stomee  */
42127bbe13Stomee public final class UserSymbolRecord implements SymbolValueRecord,
43127bbe13Stomee        Serializable, Comparable <UserSymbolRecord>
44127bbe13Stomee {
45127bbe13Stomee     static final long serialVersionUID = -591954442654439794L;
46127bbe13Stomee 
47127bbe13Stomee     static {
48127bbe13Stomee 	try {
49127bbe13Stomee 	    BeanInfo info = Introspector.getBeanInfo(UserSymbolRecord.class);
50127bbe13Stomee 	    PersistenceDelegate persistenceDelegate =
51127bbe13Stomee 		    new DefaultPersistenceDelegate(
52127bbe13Stomee 		    new String[] {"processID", "symbol", "address"})
53127bbe13Stomee 	    {
54127bbe13Stomee 		/*
55127bbe13Stomee 		 * Need to prevent DefaultPersistenceDelegate from using
56127bbe13Stomee 		 * overridden equals() method, resulting in a
57127bbe13Stomee 		 * StackOverFlowError.  Revert to PersistenceDelegate
58127bbe13Stomee 		 * implementation.  See
59127bbe13Stomee 		 * http://forum.java.sun.com/thread.jspa?threadID=
60127bbe13Stomee 		 * 477019&tstart=135
61127bbe13Stomee 		 */
62127bbe13Stomee 		protected boolean
63127bbe13Stomee 		mutatesTo(Object oldInstance, Object newInstance)
64127bbe13Stomee 		{
65127bbe13Stomee 		    return (newInstance != null && oldInstance != null &&
66127bbe13Stomee 			    oldInstance.getClass() == newInstance.getClass());
67127bbe13Stomee 		}
68127bbe13Stomee 	    };
69127bbe13Stomee 	    BeanDescriptor d = info.getBeanDescriptor();
70127bbe13Stomee 	    d.setValue("persistenceDelegate", persistenceDelegate);
71127bbe13Stomee 	} catch (IntrospectionException e) {
72127bbe13Stomee 	    e.printStackTrace();
73127bbe13Stomee 	}
74127bbe13Stomee     }
75127bbe13Stomee 
76127bbe13Stomee     // serialized explicitly to hide implementation; treat as final
77127bbe13Stomee     private transient Value value;
78127bbe13Stomee     // set natively after creation; treat as final
79127bbe13Stomee     private transient String symbol;
80127bbe13Stomee 
81127bbe13Stomee     /**
82127bbe13Stomee      * Called by native code.
83127bbe13Stomee      */
84127bbe13Stomee     private
UserSymbolRecord(int pid, long addressValue)85127bbe13Stomee     UserSymbolRecord(int pid, long addressValue)
86127bbe13Stomee     {
87127bbe13Stomee 	value = new Value(pid, addressValue);
88127bbe13Stomee     }
89127bbe13Stomee 
90127bbe13Stomee     /**
91127bbe13Stomee      * Creates a {@code UserSymbolRecord} with the given process ID,
92127bbe13Stomee      * symbol lookup, and user address converted in probe context as a
93127bbe13Stomee      * result of the DTrace {@code umod()}, {@code ufunc()}, or {@code
94127bbe13Stomee      * usym()} action.
95127bbe13Stomee      * <p>
96127bbe13Stomee      * Supports XML persistence.
97127bbe13Stomee      *
98127bbe13Stomee      * @param pid non-negative user process ID
99127bbe13Stomee      * @param lookupValue the result in the native DTrace library of
100127bbe13Stomee      * looking up the symbol associated with the given user address
101127bbe13Stomee      * @param addressValue symbol address
102127bbe13Stomee      * @throws NullPointerException if the given lookup value is {@code null}
103127bbe13Stomee      * @throws IllegalArgumentException if the given process ID is
104127bbe13Stomee      * negative
105127bbe13Stomee      */
106127bbe13Stomee     public
UserSymbolRecord(int pid, String lookupValue, long addressValue)107127bbe13Stomee     UserSymbolRecord(int pid, String lookupValue, long addressValue)
108127bbe13Stomee     {
109127bbe13Stomee 	value = new Value(pid, addressValue);
110127bbe13Stomee 	symbol = lookupValue;
111127bbe13Stomee 	validate();
112127bbe13Stomee     }
113127bbe13Stomee 
11491cfa10aStomee     private final void
validate()115127bbe13Stomee     validate()
116127bbe13Stomee     {
117127bbe13Stomee 	if (symbol == null) {
118127bbe13Stomee 	    throw new NullPointerException("symbol is null");
119127bbe13Stomee 	}
120127bbe13Stomee     }
121127bbe13Stomee 
122127bbe13Stomee     /**
123127bbe13Stomee      * Gets the process ID associated with this record's symbol.
124127bbe13Stomee      *
125127bbe13Stomee      * @return non-negative pid
126127bbe13Stomee      */
127127bbe13Stomee     public int
getProcessID()128127bbe13Stomee     getProcessID()
129127bbe13Stomee     {
130127bbe13Stomee 	return value.getProcessID();
131127bbe13Stomee     }
132127bbe13Stomee 
133127bbe13Stomee     /**
134127bbe13Stomee      * Gets the result of the address lookup in the same form returned
135127bbe13Stomee      * by {@link Consumer#lookupUserFunction(int pid, long address)}.
136127bbe13Stomee      *
137127bbe13Stomee      * @return non-null address lookup in the format defined by the
138127bbe13Stomee      * native DTrace library
139127bbe13Stomee      */
140127bbe13Stomee     public String
getSymbol()141127bbe13Stomee     getSymbol()
142127bbe13Stomee     {
143127bbe13Stomee 	return symbol;
144127bbe13Stomee     }
145127bbe13Stomee 
146127bbe13Stomee     /**
147127bbe13Stomee      * Called by native code and ProbeData addSymbolRecord()
148127bbe13Stomee      */
149127bbe13Stomee     void
setSymbol(String lookupValue)150127bbe13Stomee     setSymbol(String lookupValue)
151127bbe13Stomee     {
152127bbe13Stomee 	symbol = lookupValue;
153127bbe13Stomee 	validate();
154127bbe13Stomee     }
155127bbe13Stomee 
156127bbe13Stomee     /**
157127bbe13Stomee      * Gets the symbol's user address.
158127bbe13Stomee      *
159127bbe13Stomee      * @return the symbol's user address
160127bbe13Stomee      */
161127bbe13Stomee     public long
getAddress()162127bbe13Stomee     getAddress()
163127bbe13Stomee     {
164127bbe13Stomee 	return value.getAddress();
165127bbe13Stomee     }
166127bbe13Stomee 
167127bbe13Stomee     /**
168127bbe13Stomee      * Gets the composite value of the symbol's process ID and address.
169127bbe13Stomee      * The value is used in {@link #equals(Object o) equals()} and
170127bbe13Stomee      * {@link #compareTo(UserSymbolRecord r) compareTo()} to test
171127bbe13Stomee      * equality and to determine the natural ordering of {@code
172127bbe13Stomee      * UserSymbolRecord} instances.
173127bbe13Stomee      *
174127bbe13Stomee      * @return non-null composite value of the symbols's process ID and
175127bbe13Stomee      * address
176127bbe13Stomee      */
177127bbe13Stomee     public Value
getValue()178127bbe13Stomee     getValue()
179127bbe13Stomee     {
180127bbe13Stomee 	return value;
181127bbe13Stomee     }
182127bbe13Stomee 
183127bbe13Stomee     /**
184127bbe13Stomee      * Compares the specified object with this {@code UserSymbolRecord}
185127bbe13Stomee      * for equality.  Returns {@code true} if and only if the specified
186127bbe13Stomee      * object is also a {@code UserSymbolRecord} and both records have
187127bbe13Stomee      * the same process ID and the same address.
188127bbe13Stomee      *
189127bbe13Stomee      * @return {@code true} if and only if the specified object is also
190127bbe13Stomee      * a {@code UserSymbolRecord} and both records have the same
191127bbe13Stomee      * process ID and the same address
192127bbe13Stomee      */
193127bbe13Stomee     @Override
194127bbe13Stomee     public boolean
equals(Object o)195127bbe13Stomee     equals(Object o)
196127bbe13Stomee     {
197127bbe13Stomee 	if (o instanceof UserSymbolRecord) {
198127bbe13Stomee 	    UserSymbolRecord r = (UserSymbolRecord)o;
199127bbe13Stomee 	    return value.equals(r.value);
200127bbe13Stomee 	}
201127bbe13Stomee 	return false;
202127bbe13Stomee     }
203127bbe13Stomee 
204127bbe13Stomee     /**
205127bbe13Stomee      * Overridden to ensure that equal instances have equal hash codes.
206127bbe13Stomee      */
207127bbe13Stomee     @Override
208127bbe13Stomee     public int
hashCode()209127bbe13Stomee     hashCode()
210127bbe13Stomee     {
211127bbe13Stomee 	return value.hashCode();
212127bbe13Stomee     }
213127bbe13Stomee 
214127bbe13Stomee     /**
215127bbe13Stomee      * Compares this record with the given user symbol lookup and orders
216127bbe13Stomee      * by the combined value of process ID first and address second.
217127bbe13Stomee      * The comparison treats addresses as unsigned values so the
218127bbe13Stomee      * ordering is consistent with that defined in the native DTrace
219127bbe13Stomee      * library. The {@code compareTo()} method is compatible with {@link
220127bbe13Stomee      * #equals(Object o) equals()}.
221127bbe13Stomee      *
222127bbe13Stomee      * @return -1, 0, or 1 as this record's combined process ID and
223127bbe13Stomee      * address is less than, equal to, or greater than the given
224127bbe13Stomee      * record's combined process ID and address
225127bbe13Stomee      */
226127bbe13Stomee     public int
compareTo(UserSymbolRecord r)227127bbe13Stomee     compareTo(UserSymbolRecord r)
228127bbe13Stomee     {
229127bbe13Stomee 	return value.compareTo(r.value);
230127bbe13Stomee     }
231127bbe13Stomee 
232127bbe13Stomee     /**
233127bbe13Stomee      * Serialize this {@code UserSymbolRecord} instance.
234127bbe13Stomee      *
235127bbe13Stomee      * @serialData processID ({@code int}), followed by symbol ({@code
236127bbe13Stomee      * java.lang.String}), followed by address ({@code long})
237127bbe13Stomee      */
238127bbe13Stomee     private void
writeObject(ObjectOutputStream s)239127bbe13Stomee     writeObject(ObjectOutputStream s) throws IOException
240127bbe13Stomee     {
241127bbe13Stomee 	s.defaultWriteObject();
242127bbe13Stomee 	s.writeInt(getProcessID());
243127bbe13Stomee 	s.writeObject(getSymbol());
244127bbe13Stomee 	s.writeLong(getAddress());
245127bbe13Stomee     }
246127bbe13Stomee 
247127bbe13Stomee     private void
readObject(ObjectInputStream s)248127bbe13Stomee     readObject(ObjectInputStream s)
249127bbe13Stomee             throws IOException, ClassNotFoundException
250127bbe13Stomee     {
251127bbe13Stomee 	s.defaultReadObject();
252127bbe13Stomee 	int pid = s.readInt();
253127bbe13Stomee 	symbol = (String)s.readObject();
254127bbe13Stomee 	long addressValue = s.readLong();
255127bbe13Stomee 	try {
256127bbe13Stomee 	    value = new Value(pid, addressValue);
257127bbe13Stomee 	    validate();
258127bbe13Stomee 	} catch (Exception e) {
259*4ae67516Stomee 	    InvalidObjectException x = new InvalidObjectException(
260*4ae67516Stomee 		    e.getMessage());
261*4ae67516Stomee 	    x.initCause(e);
262*4ae67516Stomee 	    throw x;
263127bbe13Stomee 	}
264127bbe13Stomee     }
265127bbe13Stomee 
266127bbe13Stomee     /**
267127bbe13Stomee      * Gets the result of this symbol lookup.  The format is defined in
268127bbe13Stomee      * the native DTrace library and is as stable as that library
269127bbe13Stomee      * definition.
270127bbe13Stomee      *
271127bbe13Stomee      * @return {@link #getSymbol()}
272127bbe13Stomee      */
273127bbe13Stomee     @Override
274127bbe13Stomee     public String
toString()275127bbe13Stomee     toString()
276127bbe13Stomee     {
277127bbe13Stomee 	return symbol;
278127bbe13Stomee     }
279127bbe13Stomee 
280127bbe13Stomee     /**
281127bbe13Stomee      * The composite value of a symbol's process ID and user address.
282127bbe13Stomee      * <p>
283127bbe13Stomee      * Immutable.  Supports persistence using {@link
284127bbe13Stomee      * java.beans.XMLEncoder}.
285127bbe13Stomee      */
286127bbe13Stomee     public static final class Value implements Serializable,
287127bbe13Stomee 	    Comparable <Value> {
288127bbe13Stomee 	static final long serialVersionUID = -91449037747641135L;
289127bbe13Stomee 
290127bbe13Stomee 	static {
291127bbe13Stomee 	    try {
292127bbe13Stomee 		BeanInfo info = Introspector.getBeanInfo(
293127bbe13Stomee 			UserSymbolRecord.Value.class);
294127bbe13Stomee 		PersistenceDelegate persistenceDelegate =
295127bbe13Stomee 			new DefaultPersistenceDelegate(
296127bbe13Stomee 			new String[] {"processID", "address"})
297127bbe13Stomee 		{
298127bbe13Stomee 		    /*
299127bbe13Stomee 		     * Need to prevent DefaultPersistenceDelegate from using
300127bbe13Stomee 		     * overridden equals() method, resulting in a
301127bbe13Stomee 		     * StackOverFlowError.  Revert to PersistenceDelegate
302127bbe13Stomee 		     * implementation.  See
303127bbe13Stomee 		     * http://forum.java.sun.com/thread.jspa?threadID=
304127bbe13Stomee 		     * 477019&tstart=135
305127bbe13Stomee 		     */
306127bbe13Stomee 		    protected boolean
307127bbe13Stomee 		    mutatesTo(Object oldInstance, Object newInstance)
308127bbe13Stomee 		    {
309127bbe13Stomee 			return (newInstance != null && oldInstance != null &&
310127bbe13Stomee 				oldInstance.getClass() ==
311127bbe13Stomee 				newInstance.getClass());
312127bbe13Stomee 		    }
313127bbe13Stomee 		};
314127bbe13Stomee 		BeanDescriptor d = info.getBeanDescriptor();
315127bbe13Stomee 		d.setValue("persistenceDelegate", persistenceDelegate);
316127bbe13Stomee 	    } catch (IntrospectionException e) {
317127bbe13Stomee 		e.printStackTrace();
318127bbe13Stomee 	    }
319127bbe13Stomee 	}
320127bbe13Stomee 
321127bbe13Stomee 	/** @serial */
322127bbe13Stomee 	private final int processID;
323127bbe13Stomee 	/** @serial */
324127bbe13Stomee 	private final long address;
325127bbe13Stomee 
326127bbe13Stomee 	/**
327127bbe13Stomee 	 * Creates a composite value with the given user process ID and
328127bbe13Stomee 	 * symbol address.
329127bbe13Stomee 	 * <p>
330127bbe13Stomee 	 * Supports XML persistence.
331127bbe13Stomee 	 *
332127bbe13Stomee 	 * @param pid non-negative process ID
333127bbe13Stomee 	 * @param addressValue symbol address
334127bbe13Stomee 	 * @throws IllegalArgumentException if the given process ID is
335127bbe13Stomee 	 * negative
336127bbe13Stomee 	 */
337127bbe13Stomee 	public
Value(int pid, long addressValue)338127bbe13Stomee 	Value(int pid, long addressValue)
339127bbe13Stomee 	{
340127bbe13Stomee 	    processID = pid;
341127bbe13Stomee 	    address = addressValue;
342127bbe13Stomee 	    validate();
343127bbe13Stomee 	}
344127bbe13Stomee 
34591cfa10aStomee 	private final void
validate()346127bbe13Stomee 	validate()
347127bbe13Stomee 	{
348127bbe13Stomee 	    if (processID < 0) {
349127bbe13Stomee 		throw new IllegalArgumentException("process ID is negative");
350127bbe13Stomee 	    }
351127bbe13Stomee 	}
352127bbe13Stomee 
353127bbe13Stomee 	/**
354127bbe13Stomee 	 * Gets the process ID associated with this value's user
355127bbe13Stomee 	 * address.
356127bbe13Stomee 	 *
357127bbe13Stomee 	 * @return non-negative pid
358127bbe13Stomee 	 */
359127bbe13Stomee 	public int
getProcessID()360127bbe13Stomee 	getProcessID()
361127bbe13Stomee 	{
362127bbe13Stomee 	    return processID;
363127bbe13Stomee 	}
364127bbe13Stomee 
365127bbe13Stomee 	/**
366127bbe13Stomee 	 * Gets the symbol's user address.
367127bbe13Stomee 	 *
368127bbe13Stomee 	 * @return the symbol's user address
369127bbe13Stomee 	 */
370127bbe13Stomee 	public long
getAddress()371127bbe13Stomee 	getAddress()
372127bbe13Stomee 	{
373127bbe13Stomee 	    return address;
374127bbe13Stomee 	}
375127bbe13Stomee 
376127bbe13Stomee 	/**
377127bbe13Stomee 	 * Compares the specified object with this {@code
378127bbe13Stomee 	 * UserSymbolRecord.Value} for equality.  Returns {@code true}
379127bbe13Stomee 	 * if and only if the specified object is also a {@code
380127bbe13Stomee 	 * UserSymbolRecord.Value} and both values have the same process
381127bbe13Stomee 	 * ID and the same address.
382127bbe13Stomee 	 *
383127bbe13Stomee 	 * @return {@code true} if and only if the specified object is
384127bbe13Stomee 	 * also a {@code UserSymbolRecord.Value} and both values have
385127bbe13Stomee 	 * the same process ID and the same address
386127bbe13Stomee 	 */
387127bbe13Stomee 	@Override
388127bbe13Stomee 	public boolean
equals(Object o)389127bbe13Stomee 	equals(Object o)
390127bbe13Stomee 	{
391127bbe13Stomee 	    if (o instanceof Value) {
392127bbe13Stomee 		Value v = (Value)o;
393127bbe13Stomee 		return ((processID == v.processID) && (address == v.address));
394127bbe13Stomee 	    }
395127bbe13Stomee 	    return false;
396127bbe13Stomee 	}
397127bbe13Stomee 
398127bbe13Stomee 	/**
399127bbe13Stomee 	 * Overridden to ensure that equal instances have equal hash
400127bbe13Stomee 	 * codes.
401127bbe13Stomee 	 */
402127bbe13Stomee 	@Override
403127bbe13Stomee 	public int
hashCode()404127bbe13Stomee 	hashCode()
405127bbe13Stomee 	{
406127bbe13Stomee 	    int hash = 17;
407127bbe13Stomee 	    hash = 37 * hash + processID;
408127bbe13Stomee 	    hash = 37 * hash + (int)(address ^ (address >>> 32));
409127bbe13Stomee 	    return hash;
410127bbe13Stomee 	}
411127bbe13Stomee 
412127bbe13Stomee 	/**
413127bbe13Stomee 	 * Compares this value with the given {@code
414127bbe13Stomee 	 * UserSymbolRecord.Value} and orders by process ID first and
415127bbe13Stomee 	 * address second.  The comparison treats addresses as unsigned
416127bbe13Stomee 	 * values so the ordering is consistent with that defined in the
417127bbe13Stomee 	 * native DTrace library. The {@code compareTo()} method is
418127bbe13Stomee 	 * compatible with {@link #equals(Object o) equals()}.
419127bbe13Stomee 	 *
420127bbe13Stomee 	 * @return -1, 0, or 1 as this value's combined process ID and
421127bbe13Stomee 	 * address is less than, equal to, or greater than the given
422127bbe13Stomee 	 * value's combined process ID and address
423127bbe13Stomee 	 */
424127bbe13Stomee 	public int
compareTo(Value v)425127bbe13Stomee 	compareTo(Value v)
426127bbe13Stomee 	{
427127bbe13Stomee 	    int cmp;
428127bbe13Stomee 	    cmp = (processID < v.processID ? -1 :
429127bbe13Stomee 		    (processID > v.processID ? 1 : 0));
430127bbe13Stomee 	    if (cmp == 0) {
431127bbe13Stomee 		cmp = ProbeData.compareUnsigned(address, v.address);
432127bbe13Stomee 	    }
433127bbe13Stomee 	    return cmp;
434127bbe13Stomee 	}
435127bbe13Stomee 
436127bbe13Stomee 	private void
readObject(ObjectInputStream s)437127bbe13Stomee 	readObject(ObjectInputStream s)
438127bbe13Stomee 		throws IOException, ClassNotFoundException
439127bbe13Stomee 	{
440127bbe13Stomee 	    s.defaultReadObject();
441127bbe13Stomee 	    // check class invariants
442127bbe13Stomee 	    try {
443127bbe13Stomee 		validate();
444127bbe13Stomee 	    } catch (Exception e) {
445*4ae67516Stomee 		InvalidObjectException x = new InvalidObjectException(
446*4ae67516Stomee 			e.getMessage());
447*4ae67516Stomee 		x.initCause(e);
448*4ae67516Stomee 		throw x;
449127bbe13Stomee 	    }
450127bbe13Stomee 	}
451127bbe13Stomee 
452127bbe13Stomee 	/**
453127bbe13Stomee 	 * Gets a string representation of this {@code
454127bbe13Stomee 	 * UserSymbolRecord.Value} instance useful for logging and not
455127bbe13Stomee 	 * intended for display.  The exact details of the
456127bbe13Stomee 	 * representation are unspecified and subject to change, but the
457127bbe13Stomee 	 * following format may be regarded as typical:
458127bbe13Stomee 	 * <pre><code>
459127bbe13Stomee 	 * class-name[property1 = value1, property2 = value2]
460127bbe13Stomee 	 * </code></pre>
461127bbe13Stomee 	 */
462127bbe13Stomee 	public String
toString()463127bbe13Stomee 	toString()
464127bbe13Stomee 	{
465127bbe13Stomee 	    StringBuilder buf = new StringBuilder();
466127bbe13Stomee 	    buf.append(Value.class.getName());
467127bbe13Stomee 	    buf.append("[processID = ");
468127bbe13Stomee 	    buf.append(processID);
469127bbe13Stomee 	    buf.append(", address = ");
470127bbe13Stomee 	    buf.append(address);
471127bbe13Stomee 	    buf.append(']');
472127bbe13Stomee 	    return buf.toString();
473127bbe13Stomee 	}
474127bbe13Stomee     }
475127bbe13Stomee }
476