001/* An application that executes non-graphical
002 models specified on the command line and prints out statistics.
003
004 Copyright (c) 2001-2014 The Regents of the University of California.
005 All rights reserved.
006 Permission is hereby granted, without written agreement and without
007 license or royalty fees, to use, copy, modify, and distribute this
008 software and its documentation for any purpose, provided that the above
009 copyright notice and the following two paragraphs appear in all copies
010 of this software.
011
012 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
013 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
014 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
015 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
016 SUCH DAMAGE.
017
018 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
019 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
020 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
021 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
022 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
023 ENHANCEMENTS, OR MODIFICATIONS.
024
025 PT_COPYRIGHT_VERSION_2
026 COPYRIGHTENDKEY
027
028 */
029package ptolemy.actor.gui;
030
031import java.lang.reflect.Constructor;
032import java.net.URL;
033import java.util.Iterator;
034import java.util.LinkedList;
035import java.util.List;
036
037import ptolemy.actor.CompositeActor;
038import ptolemy.actor.Director;
039import ptolemy.actor.Manager;
040import ptolemy.data.expr.Variable;
041import ptolemy.kernel.attributes.VersionAttribute;
042import ptolemy.kernel.util.Attribute;
043import ptolemy.kernel.util.IllegalActionException;
044import ptolemy.kernel.util.Settable;
045import ptolemy.kernel.util.Workspace;
046import ptolemy.moml.MoMLParser;
047import ptolemy.moml.StreamErrorHandler;
048import ptolemy.moml.filter.BackwardCompatibility;
049import ptolemy.moml.filter.RemoveGraphicalClasses;
050import ptolemy.util.StringUtilities;
051
052///////////////////////////////////////////////////////////////////
053//// MoMLSimpleStatisticalApplication
054
055/** A simple application that reads in a .xml file as a command
056 line argument, runs it and prints out time and memory statistics
057
058 <p>MoMLApplication sets the look and feel, which starts up Swing,
059 so we can't use MoMLApplication for non-graphical simulations.
060
061 <p>We implement the ChangeListener interface so that this
062 class will get exceptions thrown by failed change requests.
063
064 <p>Below is an example use of this class:
065 <pre>
066 java -classpath $PTII ptolemy.actor.gui.MoMLSimpleStatisticalApplication -iterations 2 ../../../ptolemy/domains/sdf/demo/OrthogonalCom/OrthogonalCom.xml
067 </pre>
068
069 @author Christopher Hylands
070 @version $Id$
071 @since Ptolemy II 2.0
072 @Pt.ProposedRating Red (cxh)
073 @Pt.AcceptedRating Red (eal)
074 */
075public class MoMLSimpleStatisticalApplication
076        extends ptolemy.moml.MoMLSimpleApplication {
077    /** Parse the xml file and run it.
078     *  @param args The command line arguments
079     *  @exception Exception If there was a problem parsing
080     *  or running the model.
081     */
082    public MoMLSimpleStatisticalApplication(String[] args) throws Exception {
083        _parser = new MoMLParser();
084
085        MoMLParser.setErrorHandler(new StreamErrorHandler());
086
087        // The test suite calls MoMLSimpleApplication multiple times,
088        // and the list of filters is static, so we reset it each time
089        // so as to avoid adding filters every time we run an auto test.
090        // We set the list of MoMLFilters to handle Backward Compatibility.
091        MoMLParser.setMoMLFilters(BackwardCompatibility.allFilters());
092
093        // Filter out any graphical classes.
094        MoMLParser.addMoMLFilter(new RemoveGraphicalClasses());
095
096        // First, we gc and then print the memory stats
097        // BTW to get more info about gc,
098        // use java -verbose:gc . . .
099        System.gc();
100        Thread.sleep(1000);
101
102        long startTime = System.currentTimeMillis();
103
104        _parseArgs(args);
105
106        // We use parse(URL, URL) here instead of parseFile(String)
107        // because parseFile() works best on relative pathnames and
108        // has problems finding resources like files specified in
109        // parameters if the xml file was specified as an absolute path.
110        //CompositeActor toplevel = (CompositeActor) parser.parse(null,
111        //        new File(xmlFilename).toURL());
112        Manager manager = new Manager(_toplevel.workspace(),
113                "MoMLSimpleStatisticalApplication");
114        _toplevel.setManager(manager);
115        _toplevel.addChangeListener(this);
116
117        Runtime runtime = Runtime.getRuntime();
118
119        // Get the memory stats before we get the model name
120        // just to be sure that getting the model name does
121        // not skew are data too much
122        long totalMemory1 = runtime.totalMemory() / 1024;
123        long freeMemory1 = runtime.freeMemory() / 1024;
124
125        String modelName = _toplevel.getName();
126
127        System.out.println(modelName + ": Stats before execution:    "
128                + Manager.timeAndMemory(startTime, totalMemory1, freeMemory1));
129
130        // Second, we run and print memory stats.
131        Manager.minimumStatisticsTime = 0;
132        manager.execute();
133
134        long totalMemory2 = runtime.totalMemory() / 1024;
135        long freeMemory2 = runtime.freeMemory() / 1024;
136        String standardStats = Manager.timeAndMemory(startTime, totalMemory2,
137                freeMemory2);
138
139        System.out.println(
140                modelName + ": Execution stats:           " + standardStats);
141
142        // Third, we gc and print memory stats.
143        System.gc();
144        Thread.sleep(1000);
145
146        long totalMemory3 = runtime.totalMemory() / 1024;
147        long freeMemory3 = runtime.freeMemory() / 1024;
148        System.out.println(modelName + ": After Garbage Collection:  "
149                + Manager.timeAndMemory(startTime, totalMemory3, freeMemory3));
150        System.out.println(modelName + ": construction size:         "
151                + totalMemory1 + "K - " + freeMemory1 + "K = "
152                + (totalMemory1 - freeMemory1) + "K");
153        System.out.println(modelName + ": model alloc. while exec. : "
154                + freeMemory1 + "K - " + freeMemory3 + "K = "
155                + (freeMemory1 - freeMemory3) + "K");
156        System.out.println(modelName + ": model alloc. runtime data: "
157                + freeMemory3 + "K - " + freeMemory2 + "K = "
158                + (freeMemory3 - freeMemory2) + "K");
159
160        // Print out the standard stats at the end
161        // so as not to break too many scripts
162        System.out.println(
163                standardStats + " Stat: " + (totalMemory1 - freeMemory1)
164                        + "K StatRT: " + (freeMemory1 - freeMemory3)
165                        + "K DynRT: " + (freeMemory3 - freeMemory2) + "K");
166    }
167
168    /** Parse a command-line argument.
169     *  @param arg The command-line argument to be parsed.
170     *  @return True if the argument is understood, false otherwise.
171     *  @exception Exception If something goes wrong.
172     */
173    protected boolean _parseArg(String arg) throws Exception {
174        if (arg.equals("-class")) {
175            _expectingClass = true;
176        } else if (arg.equals("-help")) {
177            System.out.println(_usage());
178
179            // NOTE: This means the test suites cannot test -help
180            StringUtilities.exit(0);
181        } else if (arg.equals("-test")) {
182            _test = true;
183        } else if (arg.equals("-version")) {
184            System.out.println("Version " + VersionAttribute.CURRENT_VERSION
185                    + ", Build $Id$");
186
187            // NOTE: This means the test suites cannot test -version
188            StringUtilities.exit(0);
189        } else if (arg.equals("")) {
190            // Ignore blank argument.
191        } else {
192            if (_expectingClass) {
193                _expectingClass = false;
194
195                // Create the class.
196                Class<?> newClass = Class.forName(arg);
197
198                // Instantiate the specified class in a new workspace.
199                Workspace workspace = new Workspace();
200
201                // Get the constructor that takes a Workspace argument.
202                Class[] argTypes = new Class[1];
203                argTypes[0] = workspace.getClass();
204
205                Constructor<?> constructor = newClass.getConstructor(argTypes);
206
207                Object[] args = new Object[1];
208                args[0] = workspace;
209                constructor.newInstance(args);
210            } else {
211                if (!arg.startsWith("-")) {
212                    // Assume the argument is a file name or URL.
213                    // Attempt to read it.
214                    URL inURL = ConfigurationApplication.specToURL(arg);
215
216                    // Strangely, the XmlParser does not want as base the
217                    // directory containing the file, but rather the
218                    // file itself.
219                    URL base = inURL;
220
221                    // Assume this is a MoML file, and open it.
222                    _parser.reset();
223
224                    // Always print the open time
225                    long startTime = System.currentTimeMillis();
226                    _toplevel = (CompositeActor) _parser.parse(base, inURL);
227                    System.out.println("Opened \"" + base + "\": "
228                            + Manager.timeAndMemory(startTime));
229                    long statisticsStartTime = System.currentTimeMillis();
230                    System.out.println(_toplevel.statistics(null));
231                    long statisticsEndTime = System.currentTimeMillis();
232                    System.out.println("Generating statistics took"
233                            + (statisticsEndTime - statisticsStartTime)
234                            + " ms. ");
235                } else {
236                    // Argument not recognized.
237                    return false;
238                }
239            }
240        }
241
242        return true;
243    }
244
245    /** Parse the command-line arguments.
246     *  @param args The command-line arguments to be parsed.
247     *  @exception Exception If an argument is not understood or triggers
248     *   an error.
249     */
250    protected void _parseArgs(String[] args) throws Exception {
251        for (int i = 0; i < args.length; i++) {
252            String arg = args[i];
253
254            if (_parseArg(arg) == false) {
255                if (arg.trim().startsWith("-")) {
256                    if (i >= args.length - 1) {
257                        throw new IllegalActionException(
258                                "Cannot set " + "parameter " + arg
259                                        + " when no value is " + "given.");
260                    }
261
262                    // Save in case this is a parameter name and value.
263                    _parameterNames.add(arg.substring(1));
264                    _parameterValues.add(args[i + 1]);
265                    i++;
266                } else {
267                    // Unrecognized option.
268                    throw new IllegalActionException(
269                            "Unrecognized option: " + arg);
270                }
271            }
272        }
273
274        if (_expectingClass) {
275            throw new IllegalActionException("Missing classname.");
276        }
277
278        // Check saved options to see whether any is setting an attribute.
279        Iterator<String> names = _parameterNames.iterator();
280        Iterator<String> values = _parameterValues.iterator();
281
282        while (names.hasNext() && values.hasNext()) {
283            String name = names.next();
284            String value = values.next();
285
286            boolean match = false;
287
288            CompositeActor model = _toplevel;
289            System.out.println("model = " + model.getFullName());
290
291            Attribute attribute = model.getAttribute(name);
292
293            if (attribute instanceof Settable) {
294                match = true;
295                ((Settable) attribute).setExpression(value);
296
297                if (attribute instanceof Variable) {
298                    // Force evaluation so that listeners are notified.
299                    ((Variable) attribute).getToken();
300                }
301            }
302
303            Director director = model.getDirector();
304
305            if (director != null) {
306                attribute = director.getAttribute(name);
307
308                if (attribute instanceof Settable) {
309                    match = true;
310                    ((Settable) attribute).setExpression(value);
311
312                    if (attribute instanceof Variable) {
313                        // Force evaluation so that listeners
314                        // are notified.
315                        ((Variable) attribute).getToken();
316                    }
317                }
318            }
319
320            if (!match) {
321                // Unrecognized option.
322                throw new IllegalActionException("Unrecognized option: "
323                        + "No parameter exists with name " + name);
324            }
325        }
326    }
327
328    /** Create an instance of a single model and run it.
329     *  @param args The command-line arguments naming the .xml file to run
330     */
331    public static void main(String[] args) {
332        try {
333            new MoMLSimpleStatisticalApplication(args);
334        } catch (Exception ex) {
335            System.err.println("Command failed: " + ex);
336            ex.printStackTrace();
337        }
338    }
339
340    /** Return a string summarizing the command-line arguments.
341     *  @return A usage string.
342     */
343    protected String _usage() {
344        StringBuffer result = new StringBuffer("Usage: " + _commandTemplate
345                + "\n\n" + "Options that take values:\n");
346
347        int i;
348
349        for (i = 0; i < _commandOptions.length; i++) {
350            result.append(" " + _commandOptions[i][0] + " "
351                    + _commandOptions[i][1] + "\n");
352        }
353
354        result.append("\nBoolean flags:\n");
355
356        for (i = 0; i < _commandFlags.length; i++) {
357            result.append(" " + _commandFlags[i]);
358        }
359
360        return result.toString();
361    }
362
363    ///////////////////////////////////////////////////////////////////
364    ////                         protected variables               ////
365
366    /** The command-line options that are either present or not. */
367    protected String[] _commandFlags = { "-help", "-test", "-version", };
368
369    /** The command-line options that take arguments. */
370    protected String[][] _commandOptions = { { "-class", "<classname>" },
371            { "-<parameter name>", "<parameter value>" }, };
372
373    /** The form of the command line. */
374    protected String _commandTemplate = "java -classpath $PTII ptolemy.actor.gui.MoMLSimpleStatisticalApplication [ options ] [file ...]";
375
376    /** If true, then auto exit after a few seconds. */
377    protected static boolean _test = false;
378
379    ///////////////////////////////////////////////////////////////////
380    ////                         private variables                 ////
381    // Flag indicating that the previous argument was -class.
382    private boolean _expectingClass = false;
383
384    // List of parameter names seen on the command line.
385    private List<String> _parameterNames = new LinkedList<String>();
386
387    // List of parameter values seen on the command line.
388    private List<String> _parameterValues = new LinkedList<String>();
389}