001: /*
002: Copyright (c) 2005 - 2006, MentorGen, LLC
003: All rights reserved.
004:
005: Redistribution and use in source and binary forms, with or without
006: modification, are permitted provided that the following conditions are met:
007:
008: + Redistributions of source code must retain the above copyright notice,
009: this list of conditions and the following disclaimer.
010: + Redistributions in binary form must reproduce the above copyright notice,
011: this list of conditions and the following disclaimer in the documentation
012: and/or other materials provided with the distribution.
013: + Neither the name of MentorGen LLC nor the names of its contributors may be
014: used to endorse or promote products derived from this software without
015: specific prior written permission.
016:
017: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
018: AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
019: IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
020: ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
021: LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
022: CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
023: SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
024: INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
025: CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
026: ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
027: POSSIBILITY OF SUCH DAMAGE.
028: */
029: package com.mentorgen.tools.profile.output;
030:
031: import java.io.BufferedWriter;
032: import java.io.File;
033: import java.io.FileWriter;
034: import java.io.IOException;
035: import java.io.PrintWriter;
036: import java.text.SimpleDateFormat;
037: import java.util.Date;
038: import java.util.HashMap;
039:
040: import com.mentorgen.tools.profile.Controller;
041: import com.mentorgen.tools.profile.runtime.ClassAllocation;
042: import com.mentorgen.tools.profile.runtime.Frame;
043: import com.mentorgen.tools.profile.runtime.Profile;
044:
045: /**
046: * This class outputs the profile as an XML document. We're creating the
047: * XML document by hand rather than by using one of the standard XML
048: * libraries. We're doing it this way to avoid using non-core libraries
049: * (I'm not sure if this is warrented).
050: * <br/>
051: * Since XML can be verbose, a number of things were done to help limit
052: * the file size:
053: * <ul>
054: * <li>When outputting a stack frame, the following abbreviations are used:
055: * <ul>
056: * <li><em>cn</em> — class name</li>
057: * <li><em>mn</em> — method name</li>
058: * <li><em>c</em> — call count</li>
059: * <li><em>t</em> — total time</li>
060: * </ul>
061: * </li>
062: * <li>The full class names have been abbreviated. Usually, the
063: * abbreviation is just the class name. There is a section at the
064: * end of the XML document that maps the short class names to
065: * the fully qualifed class names.</li>
066: * </ul>
067: *
068: * @author Andrew Wilcox
069: * @see com.mentorgen.tools.profile.output.ProfileTextDump
070: */
071: final class ProfileXMLDump {
072:
073: private static HashMap<String, String> _classNameMap = new HashMap<String, String>();
074:
075: private static String _fileName;
076: private static String _date;
077:
078: static void dump() throws IOException {
079: createFileName();
080:
081: FileWriter out = new FileWriter(_fileName);
082: BufferedWriter bufferedWriter = new BufferedWriter(out);
083: PrintWriter writer = new PrintWriter(bufferedWriter);
084:
085: outputHeader(writer);
086: outputCallGraph(writer);
087: outputObjectAlloc(writer);
088: outputClassMap(writer);
089: outputEnd(writer);
090:
091: writer.flush();
092: out.close();
093: }
094:
095: private static void createFileName() {
096: File f = new File(Controller._fileName);
097: SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd-HHmmss");
098: _date = df.format(new Date());
099:
100: if (f.isDirectory()) {
101: StringBuffer b = new StringBuffer(f.getAbsolutePath());
102: b.append(File.separator);
103: b.append(_date);
104: b.append(".xml");
105: _fileName = b.toString();
106: } else {
107: String fileName = Controller._fileName.trim();
108:
109: if (fileName.endsWith(".txt")) {
110: String t = fileName;
111: t = t.substring(0, t.length() - 4);
112: StringBuffer b = new StringBuffer(t);
113: b.append(".xml");
114: _fileName = b.toString();
115: } else if (fileName.endsWith(".xml")) {
116: _fileName = fileName;
117: } else {
118: StringBuffer b = new StringBuffer(fileName);
119: b.append(".xml");
120: _fileName = b.toString();
121: }
122: }
123: }
124:
125: private static void outputHeader(PrintWriter writer)
126: throws IOException {
127: writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
128: startElement(writer, "profile", 0);
129: attribute(writer, "file", _fileName);
130: attribute(writer, "date", _date);
131: cap(writer);
132: }
133:
134: private static void outputCallGraph(PrintWriter writer)
135: throws IOException {
136:
137: for (Long threadId : Profile.threads()) {
138: startElement(writer, "thread", 1);
139: attribute(writer, "id", threadId);
140: cap(writer);
141:
142: int i = 1;
143: for (Frame f : Profile.interactions(threadId)) {
144: startElement(writer, "interaction", 2);
145: attribute(writer, "id", i++);
146: cap(writer);
147: outputFrame(writer, f, 3);
148: endElement(writer, "interaction", 2);
149: }
150:
151: endElement(writer, "thread", 1);
152: }
153: }
154:
155: private static void outputFrame(PrintWriter writer, Frame f,
156: int depth) throws IOException {
157:
158: startElement(writer, "frame", depth);
159: attribute(writer, "cn", shortenClassName(f.getClassName()));
160: attribute(writer, "mn", f.getName());
161: attribute(writer, "c", f._metrics.getCount());
162: attribute(writer, "t", f._metrics.getTotalTime());
163:
164: if (!f.hasChildren()) {
165: endElement(writer);
166: return;
167: }
168:
169: cap(writer);
170:
171: for (Frame child : f.childIterator()) {
172: outputFrame(writer, child, depth + 1);
173: }
174:
175: endElement(writer, "frame", depth);
176: }
177:
178: private static void outputObjectAlloc(PrintWriter writer)
179: throws IOException {
180: startElement(writer, "allocation", 1);
181: cap(writer);
182:
183: for (ClassAllocation alloc : Profile.allocations()) {
184: startElement(writer, "class", 2);
185: attribute(writer, "cn", shortenClassName(alloc
186: .getInternalClassName()));
187: attribute(writer, "c", alloc.getAllocCount());
188: endElement(writer);
189: }
190:
191: endElement(writer, "allocation", 1);
192: }
193:
194: private static void outputClassMap(PrintWriter writer)
195: throws IOException {
196: startElement(writer, "class-map", 1);
197: cap(writer);
198:
199: for (String key : _classNameMap.keySet()) {
200: String value = _classNameMap.get(key);
201: startElement(writer, "entry", 2);
202: attribute(writer, "s", key); //shortened name
203: attribute(writer, "f", value); // full name
204: endElement(writer);
205: }
206:
207: endElement(writer, "class-map", 1);
208: }
209:
210: //
211: // Helper methods ...
212: //
213:
214: private static void outputEnd(PrintWriter writer)
215: throws IOException {
216: endElement(writer, "profile", 0);
217: }
218:
219: private static void startElement(PrintWriter writer,
220: String element, int depth) throws IOException {
221:
222: for (int i = 0; i < depth; i++) {
223: writer.print('\t');
224: }
225:
226: writer.print("<");
227: writer.print(element);
228: }
229:
230: private static void cap(PrintWriter writer) {
231: writer.println(">");
232: }
233:
234: private static void endElement(PrintWriter writer) {
235: writer.println("/>");
236: }
237:
238: private static void endElement(PrintWriter writer, String element,
239:
240: int depth) throws IOException {
241: for (int i = 0; i < depth; i++) {
242: writer.print('\t');
243: }
244:
245: writer.print("</");
246: writer.print(element);
247: writer.println(">");
248: }
249:
250: private static void attribute(PrintWriter writer, String name,
251: Object value) throws IOException {
252: writer.print(" ");
253: writer.print(name);
254: writer.print("=\"");
255:
256: char[] string = value.toString().toCharArray();
257:
258: for (char c : string) {
259: if (c == '<') {
260: writer.print("<");
261: } else if (c == '>') {
262: writer.print(">");
263: } else {
264: writer.print(c);
265: }
266: }
267:
268: writer.print("\"");
269: }
270:
271: private static String shortenClassName(String className) {
272: StringBuffer shortName = new StringBuffer();
273: char[] string = className.toCharArray();
274:
275: for (int i = string.length - 1; i >= 0; i--) {
276:
277: if (string[i] == '/') {
278: String fullName = _classNameMap.get(shortName
279: .toString());
280:
281: if (fullName == null) {
282: _classNameMap.put(shortName.toString(), className);
283: return shortName.toString();
284: }
285:
286: if (fullName.equals(className)) {
287: return shortName.toString();
288: }
289: }
290:
291: shortName.insert(0, string[i]);
292: }
293:
294: return className;
295: }
296:
297: }
|