001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.cldchi.tools.memoryprofiler.data;
028:
029: import com.sun.cldchi.tools.memoryprofiler.jdwp.VMConnection;
030: import com.sun.cldchi.tools.memoryprofiler.jdwp.VMReply;
031: import com.sun.cldchi.tools.memoryprofiler.jdwp.BoundException;
032: import com.sun.cldchi.tools.memoryprofiler.jdwp.DebugeeException;
033:
034: import java.net.*;
035: import java.io.*;
036: import java.util.*;
037:
038: class GlobalData implements MPDataProvider {
039: private VMConnection _connector;
040: private int _heap_start;
041: private int _heap_top;
042: private int _old_gen_end;
043: private int _allocation_top;
044: private HashMap _allJavaObjects = new HashMap();
045: private HashMap _allClasses = new HashMap();
046:
047: // memory profiler command constants
048: private static final int MPGetGlobalData = 0x1201;
049: private static final int MPGetHeapData = 0x1202;
050: private static final int MPGetClasses = 0x1203;
051: private static final int MPGetRoots = 0x1204;
052: private static final int MPVMSuspend = 0x1205;
053: private static final int MPVMResume = 0x1206;
054: private static final int MPVMStackTrace = 0x1207;
055:
056: //presentation strings
057: public static final String InternalObjectName = "VM Internal object";
058: public static final String StaticsObjectName = "Statics of class ";
059: public static final String StackObjectName = "Stack object";
060:
061: private static final int CLASS_ID_OFFSET = 0;
062: private static final int TASK_ID_OFFSET = 16;
063: private static final int OBJ_TYPE_OFFSET = 23;
064:
065: public int get_heap_start() {
066: return _heap_start;
067: };
068:
069: public int get_heap_top() {
070: return _heap_top;
071: };
072:
073: public int get_old_gen_end() {
074: return _old_gen_end;
075: };
076:
077: public int get_allocation_top() {
078: return _allocation_top;
079: };
080:
081: GlobalData(VMConnection connection) {
082: _connector = connection;
083: }
084:
085: private void update() throws SocketException {
086: getClassList();
087: getAllData();
088: getRoots();
089: try {
090: VMReply r = _connector.sendReplyCommand(MPGetGlobalData);
091: _heap_start = r.getInt();
092: _heap_top = r.getInt();
093: _old_gen_end = r.getInt();
094: _allocation_top = r.getInt();
095: } catch (DebugeeException e) {
096: reset();
097: throw new SocketException(e.getMessage());
098: } catch (BoundException e) {
099: reset();
100: throw new SocketException(e.getMessage());
101: }
102: calculateDeadObjects();
103: }
104:
105: public JavaClass[] getClassList() throws SocketException {
106: _allClasses.clear();
107: JavaClass[] result = null;
108: try {
109: VMReply r = _connector.sendReplyCommand(MPGetClasses);
110: int classesCount = r.getInt();
111: result = new JavaClass[classesCount];
112: for (int i = 0; i < classesCount; i++) {
113: int class_id = (int) r.getInt();
114: String class_name = objectTypeNameFromJNI(r.getString());
115: JavaClass new_item = new JavaClass(class_id, class_name);
116: _allClasses.put(new Integer(class_id), new_item);
117: result[i] = new_item;
118: }
119: } catch (Exception e) {
120: reset();
121: throw new SocketException(e.getMessage());
122: }
123: return result;
124: }
125:
126: private void getAllData() throws SocketException {
127: int read = 1;
128: int oread = 1;
129: _allJavaObjects.clear();
130: try {
131: while (true) {
132: VMReply r = _connector.sendReplyCommand(MPGetHeapData);
133: int object_address = r.getInt();
134: while (object_address != -1 && object_address != -2) {
135: int size = r.getInt();
136: int mp_class_id = r.getInt();
137: int object_type = mp_class_id >> OBJ_TYPE_OFFSET;
138: int stack_number = -1;
139: if (object_type == STACK_OBJECT) {
140: stack_number = r.getInt();
141: read++;
142: }
143: int links = r.getInt();
144: int[] refs = new int[links];
145: HashMap offsets = new HashMap(links);
146: if (object_type == STACK_OBJECT) {
147: for (int i = 0; i < links; i++) {
148: refs[i] = r.getInt();
149: int offset = r.getInt();
150: Integer cur_offset = (Integer) offsets
151: .get(new Integer(refs[i]));
152: if (cur_offset == null
153: || cur_offset.intValue() < offset) {
154: offsets.put(new Integer(refs[i]),
155: new Integer(offset));
156: }
157: }
158: read += links;
159: } else {
160: for (int i = 0; i < links; i++) {
161: refs[i] = r.getInt();
162: }
163: }
164: mp_class_id = mp_class_id & 0x7FFFFF;
165: JavaClass class_item = (JavaClass) _allClasses
166: .get(new Integer(mp_class_id));
167: int class_id = mp_class_id;
168: if (class_item != null) {
169: class_id = class_item.id;
170: }
171: if (object_type == JAVA_OBJECT) {
172: _allJavaObjects.put(
173: new Integer(object_address),
174: new JavaObject(object_address,
175: class_id, size, refs,
176: JAVA_OBJECT));
177: } else if (object_type == STATICS_OBJECT) {
178: _allJavaObjects.put(
179: new Integer(object_address),
180: new JavaObject(object_address,
181: class_id, size, refs,
182: STATICS_OBJECT));
183: } else if (object_type == STACK_OBJECT) {
184: _allJavaObjects.put(
185: new Integer(object_address),
186: new JavaObject(object_address, -1,
187: size, refs, STACK_OBJECT,
188: offsets, stack_number));
189: } else if (object_type == VM_OBJECT) {
190: _allJavaObjects.put(
191: new Integer(object_address),
192: new JavaObject(object_address, -1,
193: size, refs, VM_OBJECT));
194: } else {
195: System.out
196: .println("Wrong response from VM! Unknown object type. Skipped!");
197: }
198: object_address = r.getInt();
199: read += 4;
200: read += links;
201: oread++;
202: }
203: if (object_address == -1)
204: break;
205: }
206: } catch (Exception e) {
207: reset();
208: throw new SocketException(e.getMessage());
209: }
210: updateAllObjects();
211: }
212:
213: private void updateAllObjects() {
214: for (Iterator it = _allJavaObjects.values().iterator(); it
215: .hasNext();) {
216: JavaObject obj = (JavaObject) it.next();
217: for (int i = 0; i < obj._references_addresses.length; i++) {
218: JavaObject ref = getObjectByAddress(obj._references_addresses[i]);
219: if (ref != null) {
220: obj.add_reference(ref);
221: ref.add_referee(obj);
222: }
223: }
224: }
225: }
226:
227: private JavaObject getObjectByAddress(int address) {
228: Integer key = new Integer(address);
229: return (JavaObject) _allJavaObjects.get(key);
230: }
231:
232: public JavaObject[] getObjectsOfClass(JavaClass jc) {
233: if (jc == null)
234: return new JavaObject[0];
235: int obj_count = 0;
236: int class_id = jc.id;
237:
238: for (Iterator it = _allJavaObjects.values().iterator(); it
239: .hasNext();) {
240: JavaObject elem = (JavaObject) it.next();
241: if (elem.object_type == JAVA_OBJECT
242: && elem.class_id == class_id)
243: obj_count++;
244: }
245: JavaObject result[] = new JavaObject[obj_count];
246: obj_count = 0;
247: for (Iterator it = _allJavaObjects.values().iterator(); it
248: .hasNext();) {
249: JavaObject elem = (JavaObject) it.next();
250: if (elem.object_type == JAVA_OBJECT
251: && elem.class_id == class_id)
252: result[obj_count++] = elem;
253: }
254: return result;
255: }
256:
257: public Iterator getObjects() {
258: return _allJavaObjects.values().iterator();
259: }
260:
261: public void connect(String hostName, int port)
262: throws java.net.ConnectException, SocketException {
263: _connector.connect(hostName, port);
264: update();
265: }
266:
267: public String getObjectTypeName(JavaObject obj) {
268: if (obj.object_type == JAVA_OBJECT
269: || obj.object_type == STATICS_OBJECT) {
270: JavaClass item = (JavaClass) _allClasses.get(new Integer(
271: obj.class_id));
272: if (item == null) { //this is just for debug!
273: System.out.println(obj.class_id);
274: System.out.println(obj.object_type);
275: return "null!";
276: }
277: if (obj.object_type == JAVA_OBJECT) {
278: return item.name;
279: } else { //statics object
280: return StaticsObjectName + item.name;
281: }
282: } else if (obj.object_type == STACK_OBJECT) {
283: return StackObjectName;
284: } else if (obj.object_type == VM_OBJECT) {
285: return InternalObjectName;
286: } else {
287: return "Wrong object type! Report a bug please!";
288: }
289: }
290:
291: private void getRoots() throws SocketException {
292: try {
293: VMReply r = _connector.sendReplyCommand(MPGetRoots);
294: int root = r.getInt();
295: while (root != -1) {
296: Integer key = new Integer(root);
297: JavaObject obj = (JavaObject) _allJavaObjects.get(key);
298: if (obj != null) {
299: obj.setRootDistance(0);
300: }
301: root = r.getInt();
302: }
303: } catch (Exception e) {
304: reset();
305: throw new SocketException(e.getMessage());
306: }
307:
308: }
309:
310: private void calculateDeadObjects() {
311: boolean was_updated = true;
312: while (was_updated) {
313: was_updated = false;
314: for (Iterator it = _allJavaObjects.values().iterator(); it
315: .hasNext();) {
316: JavaObject elem = (JavaObject) it.next();
317: if (elem.getRootDistance() == -1)
318: continue;
319: Object[] refs = elem.get_references();
320: for (int i = 0; i < refs.length; i++) {
321: JavaObject obj = (JavaObject) refs[i];
322: if (obj.getRootDistance() == -1) {
323: obj.setRootDistance(elem.getRootDistance() + 1);
324: was_updated = true;
325: } else if (obj.getRootDistance() > 1 + elem
326: .getRootDistance()) {
327: obj.setRootDistance(elem.getRootDistance() + 1);
328: was_updated = true;
329: }
330: }
331: }
332: }
333: }
334:
335: public JavaObject[] pathFromTheRoot(JavaObject obj) {
336: if (obj == null)
337: return null;
338: if (obj.getRootDistance() == -1)
339: return null;
340: JavaObject[] result = new JavaObject[obj.getRootDistance() + 1];
341: int dst = obj.getRootDistance();
342: while (dst > 0) {
343: result[dst--] = obj;
344: Object[] referees = obj.get_referees();
345: for (int i = 0; i < referees.length; i++) {
346: JavaObject elem = (JavaObject) referees[i];
347: if (elem.getRootDistance() == obj.getRootDistance() - 1) {
348: obj = elem;
349: break;
350: }
351: }
352: if (dst != obj.getRootDistance())
353: throw new RuntimeException("problem here!");
354: }
355: result[dst--] = obj;
356: return result;
357: }
358:
359: public JavaObject[] getObjectsFromTheAddresses(int start, int end) {
360: if (start > end)
361: throw new RuntimeException();
362: int obj_count = 0;
363: for (Iterator it = _allJavaObjects.values().iterator(); it
364: .hasNext();) {
365: JavaObject obj = (JavaObject) it.next();
366: if (obj.address + obj.size > start && obj.address < end) {
367: obj_count++;
368: }
369: }
370: JavaObject[] result = new JavaObject[obj_count];
371: obj_count = 0;
372: for (Iterator it = _allJavaObjects.values().iterator(); it
373: .hasNext();) {
374: JavaObject obj = (JavaObject) it.next();
375: if (obj.address + obj.size > start && obj.address < end) {
376: result[obj_count++] = obj;
377: }
378: }
379: Arrays.sort(result, new Comparator() {
380: public int compare(Object o1, Object o2) {
381: return ((JavaObject) o1).address
382: - ((JavaObject) o2).address;
383: }
384: });
385: return result;
386: }
387:
388: public void pauseVM() throws SocketException {
389: try {
390: _connector.sendCommand(MPVMSuspend);
391: update();
392: } catch (Exception e) {
393: reset();
394: throw new SocketException(e.getMessage());
395: }
396: }
397:
398: public void resumeVM() throws SocketException {
399: try {
400: reset();
401: _connector.sendCommand(MPVMResume);
402: } catch (Exception e) {
403: reset();
404: throw new SocketException(e.getMessage());
405: }
406: }
407:
408: public ClassStatistics[] calculateStatistics() {
409:
410: ClassStatistics.reset();
411: int count = _allClasses.values().size() + 1;
412: HashMap result = new HashMap(count);
413: for (Iterator it = _allClasses.values().iterator(); it
414: .hasNext();) {
415: JavaClass item = (JavaClass) it.next();
416: result.put(new Integer(item.id), new ClassStatistics(
417: item.name));
418: }
419: result.put(new Integer(-1), new ClassStatistics(
420: "Internal VM Objects"));
421: for (Iterator it = _allJavaObjects.values().iterator(); it
422: .hasNext();) {
423: JavaObject obj = (JavaObject) it.next();
424: int type_id = obj.class_id;
425: if (obj.object_type != JAVA_OBJECT) {
426: type_id = -1;
427: }
428: ClassStatistics cls = (ClassStatistics) result
429: .get(new Integer(type_id));
430: if (cls == null)
431: continue; //shall not happend
432: cls.add(obj, get_old_gen_end());
433: }
434: Object[] arr = result.values().toArray();
435: Arrays.sort(arr, new Comparator() {
436: public int compare(Object obj1, Object obj2) {
437: ClassStatistics cls1 = (ClassStatistics) obj1;
438: ClassStatistics cls2 = (ClassStatistics) obj2;
439: return cls2.getHeapPercentage()
440: - cls1.getHeapPercentage();
441: }
442: });
443: ClassStatistics[] res = new ClassStatistics[arr.length];
444: for (int i = 0; i < res.length; i++) {
445: res[i] = (ClassStatistics) arr[i];
446: }
447:
448: return res;
449: }
450:
451: /**
452: * Converts object type name from JNI form to <code>javap</code>-like
453: * form (for example, <code>Ljava/lang/String;</code> will be converted to
454: * <code>java.lang.String</code>). This method is
455: * used by <code>fields</code> and <code>methods</code> KJDB commands.
456: *
457: * @param jniTypeName an object type name in JNI form
458: * @return an object type name in <code>javap</code>-like form
459: */
460: private String objectTypeNameFromJNI(String jniTypeName) {
461: if (jniTypeName.indexOf("L") != 0
462: || jniTypeName.lastIndexOf(";") != jniTypeName.length() - 1) {
463: return jniTypeName;
464: }
465: char[] chars = new char[jniTypeName.length() - 2];
466: jniTypeName.getChars(1, jniTypeName.length() - 1, chars, 0);
467: for (int i = 0; i < chars.length; i++) {
468: if (chars[i] == '/') {
469: chars[i] = '.';
470: }
471: }
472: return new String(chars);
473: }
474:
475: public void closeConnections() {
476: reset();
477: _connector.closeConnections();
478: }
479:
480: private void setConnector(VMConnection connector) {
481: if (_connector == null) {
482: _connector = connector;
483: return;
484: }
485: if (_connector.isConnected()) {
486: throw new IllegalStateException(
487: "Another connector is connected to the VM");
488: }
489: _connector = connector;
490: reset();
491: }
492:
493: public String getStackTrace(JavaObject stack_object, int ptr)
494: throws SocketException {
495: if (stack_object.object_type != STACK_OBJECT) {
496: return "JavaObject of wrong type type was passed to MPDataProvider.getStackTrace.\n"
497: + "Please report a bug!";
498: }
499: int[] params = new int[2];
500: params[0] = stack_object._stack_id;
501: Integer offset = (Integer) stack_object._stack_offsets
502: .get(new Integer(ptr));
503: params[1] = offset.intValue();
504: String result = null;
505: try {
506: VMReply reply = _connector.sendReplyCommand(MPVMStackTrace,
507: params);
508: result = reply.getString();
509: } catch (Exception e) {
510: result = "VM connection is broken!";
511: reset();
512: throw new SocketException(e.getMessage());
513: }
514: return result;
515: }
516:
517: private void reset() {
518: _heap_start = 0;
519: _heap_top = 0;
520: _old_gen_end = 0;
521: _allocation_top = 0;
522: _allJavaObjects.clear();
523: _allClasses.clear();
524: ClassStatistics.reset();
525: }
526: }
|