001: /*
002: * This file or a portion of this file is licensed under the terms of
003: * the Globus Toolkit Public License, found in file GTPL, or at
004: * http://www.globus.org/toolkit/download/license.html. This notice must
005: * appear in redistributions of this file, with or without modification.
006: *
007: * Redistributions of this Software, with or without modification, must
008: * reproduce the GTPL in: (1) the Software, or (2) the Documentation or
009: * some other similar material which is provided with the Software (if
010: * any).
011: *
012: * Copyright 1999-2004 University of Chicago and The University of
013: * Southern California. All rights reserved.
014: */
015:
016: package org.griphyn.cPlanner.visualize.spaceusage;
017:
018: import org.griphyn.cPlanner.common.LogManager;
019:
020: import java.util.List;
021: import java.util.Iterator;
022:
023: import java.io.PrintWriter;
024: import java.io.FileWriter;
025: import java.io.File;
026: import java.io.IOException;
027:
028: import java.util.List;
029: import java.util.ArrayList;
030:
031: import java.text.NumberFormat;
032: import java.text.DecimalFormat;
033:
034: /**
035: * An implementation that plots in the Ploticus format.
036: *
037: * @author Karan Vahi
038: * @version $Revision: 50 $
039: */
040:
041: public class Ploticus implements Plot {
042:
043: /**
044: * The size of an empty directory as reported by du -s
045: */
046: public static final String EMPTY_DIRECTORY_SIZE = "4K";
047:
048: /**
049: * The default timing units.
050: */
051: public static final String DEFAULT_TIMING_UNITS = "seconds";
052:
053: /**
054: * The minutes unit for the x axis.
055: */
056: public static final String MINUTES_TIMING_UNITS = "minutes";
057:
058: /**
059: * The minutes unit for the x axis.
060: */
061: public static final String HOURS_TIMING_UNITS = "hours";
062:
063: /**
064: * The directory where the files are to be generated.
065: *
066: */
067: private String mDirectory;
068:
069: /**
070: * The basename of the files.
071: */
072: private String mBasename;
073:
074: /**
075: * A boolean indicating whether to use stat info or not.
076: */
077: private boolean mUseStatInfo;
078:
079: /**
080: * The handle to the logging object.
081: */
082: private LogManager mLogger;
083:
084: /**
085: * The number formatter to format the float entries.
086: */
087: private NumberFormat mNumFormatter;
088:
089: /**
090: * The time units.
091: */
092: private String mTimeUnits;
093:
094: /**
095: * The default constructor.
096: *
097: */
098: public Ploticus() {
099: mLogger = LogManager.getInstance();
100: mDirectory = ".";
101: mBasename = "ploticus";
102: mNumFormatter = new DecimalFormat("0.000");
103: mTimeUnits = this .DEFAULT_TIMING_UNITS;
104: }
105:
106: /**
107: * Initializer method.
108: *
109: * @param directory the directory where the plots need to be generated.
110: * @param basename the basename for the files that are generated.
111: * @param useStatInfo boolean indicating whether to use stat info or not.
112: */
113: public void initialize(String directory, String basename,
114: boolean useStatInfo) {
115: mDirectory = directory;
116: mBasename = basename;
117: mUseStatInfo = useStatInfo;
118: }
119:
120: /**
121: * Plot out the space usage. Writes out a Ploticus data file.
122: *
123: * @param su the SpaceUsage.
124: * @param unit the unit in which we need to plot. (K,M,G)
125: * @param timeUnits the time unit.
126: *
127: * @return List of file pathnames for the files that are written out.
128: *
129: * @exception IOException in case of unable to write to the file.
130: */
131: public List plot(SpaceUsage su, char unit, String timeUnits)
132: throws IOException {
133: //first let us sort on the timestamps
134: su.sort();
135:
136: String site;
137:
138: List result = new ArrayList(2);
139:
140: //sanity check on time units
141: mTimeUnits = (timeUnits == null) ? this .DEFAULT_TIMING_UNITS
142: : timeUnits;
143:
144: //get the size of the empty directory in appropriate units
145: float empty = new Space(new java.util.Date(),
146: this .EMPTY_DIRECTORY_SIZE).getSize(unit);
147:
148: //go thru space usage for each site.
149: for (Iterator it = su.siteIterator(); it.hasNext();) {
150: site = (String) it.next();
151:
152: String dataFile = getFilename(site, ".dat");
153: String scriptFile = getFilename(site, ".pl");
154: result.add(dataFile);
155: result.add(scriptFile);
156:
157: PrintWriter dataPW = new PrintWriter(new FileWriter(
158: dataFile));
159: mLogger.log("Will write out to " + dataFile + ","
160: + scriptFile, LogManager.DEBUG_MESSAGE_LEVEL);
161:
162: float cummulative_cln_size = 0;//tracks the space that has been cleaned up
163: float curr_size = 0; //stores the current size
164: float clnup = 0;
165:
166: boolean first = true;
167: long minTime = 0, absTime, time = 0; //in seconds
168:
169: float maxSpace = 0;
170: float cTime = 0;
171:
172: //go through space usage for a particular site
173: for (Iterator sizeIT = su.getSizes(site).iterator(); sizeIT
174: .hasNext();) {
175: Space s = (Space) sizeIT.next();
176: absTime = s.getDate().getTime();
177: curr_size = s.getSize(unit);
178:
179: if (first) {
180: minTime = absTime;
181: first = false;
182: }
183:
184: //if the difference is >0 means data was cleaned up
185: //add to cummulative size
186: //cummulative_cln_size += ( diff > 0 ) ? diff : 0;
187:
188: //calculate the relative time in seconds
189: time = (absTime - minTime) / 1000;
190:
191: //convert time from seconds to units specified
192: cTime = convertFromSecondsTo(time, timeUnits);
193:
194: //if data is regarding amount cleaned up add to cummulative size
195: if (s.getCleanupFlag()) {
196: //subtract 4K overhead of directory size
197: //only if not use statinfo
198: clnup = mUseStatInfo ? curr_size : curr_size
199: - empty;
200: cummulative_cln_size += clnup;
201:
202: mLogger.log(cTime + " job " + s.getAssociatedJob()
203: + " cleans up " + clnup + unit,
204: LogManager.DEBUG_MESSAGE_LEVEL);
205: mLogger.log(" Cummulative cleaned up size is now "
206: + cummulative_cln_size,
207: LogManager.DEBUG_MESSAGE_LEVEL);
208:
209: //do not log just proceed
210: continue;
211: }
212:
213: //update the max space
214: if (cummulative_cln_size + curr_size > maxSpace) {
215: maxSpace = cummulative_cln_size + curr_size;
216: }
217:
218: //log the entry in the data file.
219: String entry = constructEntry(s.getAssociatedJob(),
220: cTime, curr_size,
221: (cummulative_cln_size + curr_size));
222: mLogger.log(entry, LogManager.DEBUG_MESSAGE_LEVEL);
223: dataPW.println(entry);
224:
225: }
226:
227: //the value in time right now it the max time
228: generateScriptFile(scriptFile, dataFile,
229: new Character(unit).toString(), cTime, maxSpace);
230:
231: //close and flush to file per site
232: dataPW.close();
233: }
234:
235: return result;
236: }
237:
238: /**
239: * Generates the script file required to give as input to ploticus.
240: *
241: * @param name the path to the script file.
242: * @param dataFile the path to corresponding data file.
243: * @param yUnits the units for the space value.
244: * @param maxTime the time in seconds.
245: * @param maxSpace the maximum space.
246: */
247: public void generateScriptFile(String name, String dataFile,
248: String yUnits, float maxTime, float maxSpace)
249: throws IOException {
250:
251: PrintWriter writer = new PrintWriter(new FileWriter(name));
252:
253: //write the page proc
254: writer.println("#proc page");
255: writer.println("#if @DEVICE in png,gif");
256: writer.println("\t scale: 0.6");
257: writer.println("#endif");
258: writer.println();
259:
260: //write the getdata proc
261: writer.println("#proc getdata");
262: writer.print("file: ");
263: writer.println(new File(dataFile).getName());
264: writer
265: .println(" fieldnames: time with_cleanup without_cleanup jobname");
266: writer.println();
267:
268: //write out area defn
269: writer.println("#proc areadef");
270: writer.println("title: Remote Storage used over time");
271: writer.println("titledetails: size=14 align=C");
272: writer.println("rectangle: 1 1 8 4");
273:
274: /* we let ploticus worry about ranges */
275: // writer.print( "xrange: 0 " );
276: // //round to the latest 100
277: // long modTime = ( maxTime/100 + 1 )* 100 ;
278: // //round space to latest 100 if > 0
279: // float modSpace = maxSpace > 1 ?
280: // (new Float(maxSpace/100).intValue() + 1)* 100:
281: // maxSpace;
282: // writer.println( modTime );
283: // writer.print( "yrange: 0 " );
284: // writer.println( modSpace );
285: // writer.println();
286: writer.println("xautorange datafield=1");
287: writer.println("yautorange datafield=3 lowfix=0");//y axis always starts from 0
288: writer.println();
289:
290: //round to the latest 100
291: float modTime = (maxTime / 100 + 1) * 100;
292: //round space to latest 100 if > 0
293: float modSpace = maxSpace > 1 ? (new Float(maxSpace / 100)
294: .intValue() + 1) * 100 : maxSpace;
295:
296: //we want 15-16 points on the x axis
297: float xIncrement = ((modTime / 150) + 1) * 10;
298: writer.println("#proc xaxis");
299: writer.print("stubs: inc ");
300: writer.println((int) xIncrement);
301: writer.print("minorticinc: ");
302: writer.println((int) (xIncrement / 2));
303: writer.print("label: time in ");
304: writer.println(mTimeUnits);
305: writer.println();
306:
307: //we want 10 points on the y axis
308: float yIncrement = modSpace > 1 ? ((modSpace / 100) + 1) * 10
309: : modSpace / 10;
310: writer.println("#proc yaxis");
311: writer.print("stubs: inc ");
312: writer.println(yIncrement);
313: writer.print("minorticinc: ");
314: writer.println(yIncrement / 2);
315: writer.println("gridskip: min");
316: //writer.println( "ticincrement: 100 1000" );
317: writer.println("label: space used in " + yUnits);
318: writer.println("labeldistance: 0.6");
319: writer.println();
320:
321: writer.println("#proc lineplot");
322: writer.println("xfield: time");
323: writer.println("yfield: with_cleanup");
324: writer.println("linedetails: color=blue width=.5");
325: writer.println("legendlabel: with cleanup ");
326: writer.println();
327:
328: //generate the cleanup jobs using a scatter plot
329: writer.println("#proc scatterplot");
330: writer.println("xfield: time");
331: writer.println("yfield: with_cleanup");
332: writer
333: .println("symbol: shape=circle fillcolor=red radius=0.04");
334: writer.println("select: @@jobname like cln_*");
335: writer.println("legendlabel: cleanup nodes");
336: writer.println();
337: // using scatter plot now
338: // //we want only the cleanup jobs to appear
339: // writer.println( "pointsymbol: shape=circle fillcolor=blue radius=0.0" );
340: // writer.println( "altsymbol: shape=circle fillcolor=red radius=0.04" );
341: // writer.println( "altwhen: @@jobname like cln_*" );//only plot points for cleanup jobs
342: // writer.println();
343:
344: writer.println("#proc lineplot");
345: writer.println("xfield: time");
346: writer.println("yfield: without_cleanup");
347: writer
348: .println("linedetails: style=1 dashscale=3 color=green width=.5");
349: writer.println("legendlabel: without cleanup ");
350:
351: writer.println();
352:
353: writer.println("#proc legend");
354: writer.println("location: min+1 max+0.5");
355: writer.println("format: singleline");
356:
357: writer.close();
358: }
359:
360: /**
361: * Returns the filename of the ploticus file to be generated.
362: *
363: * @param site the site handle.
364: * @param suffix the suffix to be applied to the file.
365: *
366: * @return the path to the file.
367: */
368: protected String getFilename(String site, String suffix) {
369: StringBuffer sb = new StringBuffer();
370: sb.append(mDirectory).append(File.separator).append(mBasename)
371: .append("-").append(site).append(suffix);
372:
373: return sb.toString();
374: }
375:
376: /**
377: * Returns an entry that needs to be plotted in the graph.
378: *
379: * @param jobname the name of the associated job.
380: * @param time the time
381: * @param clnup_size the size with cleanup
382: * @param no_clnup_size the size without cleanup
383: *
384: * @return the entry to be logged
385: */
386: protected String constructEntry(String job, float time,
387: float clnup_size, float no_clnup_size) {
388:
389: StringBuffer sb = new StringBuffer();
390: sb.append(mNumFormatter.format(time)).append("\t").append(
391: mNumFormatter.format(clnup_size)).append("\t").append(
392: mNumFormatter.format(no_clnup_size)).append("\t")
393: .append(job);
394:
395: return sb.toString();
396: }
397:
398: /**
399: * Converts from seconds to one of the units specified.
400: *
401: * @param time the time.
402: * @param units the units
403: *
404: * @return converted value in long.
405: */
406: private float convertFromSecondsTo(long time, String units) {
407: if (!validTimeUnits(units)) {
408: throw new RuntimeException("Unsupported time units "
409: + units);
410: }
411:
412: if (units == this .DEFAULT_TIMING_UNITS) {
413: return time;
414: }
415:
416: float result;
417:
418: float factor = (units.equals(this .MINUTES_TIMING_UNITS)) ? 60
419: : (units.equals(HOURS_TIMING_UNITS)) ? 3600 : -1;
420:
421: result = (time / (int) factor + (time % factor) / factor);
422:
423: return result;
424: }
425:
426: /**
427: * Returns a boolean indicating if a valid time unit or not.
428: *
429: * @param unit the time unit.
430: *
431: * @return boolean
432: */
433: private boolean validTimeUnits(String units) {
434:
435: return (units.equals(this.DEFAULT_TIMING_UNITS)
436: || units.equals(this.MINUTES_TIMING_UNITS) || units
437: .equals(this.HOURS_TIMING_UNITS));
438:
439: }
440: }
|