source: branches/dev-jw-2590/Compiler/ModelicaMiddleEnd/src/jastadd/Profiling.jrag @ 13900

Last change on this file since 13900 was 13900, checked in by jwedin, 7 weeks ago

Changed so that node count dumping is done on the root node instead of statically on ASTNode. Refactored some fields to no longer be static. Changed so that DSSets are given their id in the constructor. #5865

File size: 29.2 KB
Line 
1/*
2    Copyright (C) 2009 Modelon AB
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, version 3 of the License.
7
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12
13    You should have received a copy of the GNU General Public License
14    along with this program.  If not, see <http://www.gnu.org/licenses/>.
15*/
16
17import java.io.PrintStream;
18import java.lang.reflect.Field;
19import java.lang.reflect.Modifier;
20import java.security.AccessController;
21import java.security.PrivilegedAction;
22import java.security.PrivilegedExceptionAction;
23import java.util.ArrayList;
24import java.util.Collections;
25import java.util.Comparator;
26import java.util.HashMap;
27import java.util.IdentityHashMap;
28import java.util.Iterator;
29import java.util.Map;
30import java.util.Map.Entry;
31import org.jmodelica.util.Criteria;
32import org.jmodelica.util.streams.NullStream;
33
34aspect LightProfiling {
35       
36        private static StepInfo ASTNode.stepInfo = new StepInfo();
37       
38        public static StepInfo ASTNode.getStepInfo() {
39                return stepInfo;
40        }
41       
42        public static void ASTNode.beginStep(String name) {
43                stepInfo.begin(name);
44        }
45       
46        public static void ASTNode.endStep(String name) {
47                stepInfo.end(name);
48        }
49       
50        public class StepInfo {
51               
52                public static boolean GC_BEFORE_MEM = false;
53               
54                public static final Class<? extends InfoItem> TIME = TimeItem.class;
55                public static final Class<? extends InfoItem> MEMORY_CHANGE = MemoryChangeItem.class;
56               
57                private ArrayList<Class<? extends InfoItem>> itemClasses;
58                private ArrayList<InfoNode> open;
59                private InfoNode top;
60               
61                public StepInfo() {
62                        itemClasses = new ArrayList<Class<? extends InfoItem>>();
63                        itemClasses.add(TIME);
64                        itemClasses.add(MEMORY_CHANGE);
65                        reset();
66                }
67               
68                public void reset() {
69                        open = new ArrayList<InfoNode>();
70                        top = new InfoNode("Total", 0);
71                        open.add(top);
72                        top.begin();
73                }
74               
75                public void addItemType(Class<? extends InfoItem> item) {
76                        itemClasses.add(item);
77                }
78               
79                public void clearItemTypes() {
80                        itemClasses.clear();
81                }
82               
83                public void begin(String name) {
84                        if (name == null)
85                                throw new IllegalArgumentException("Must give a name");
86                        InfoNode n = new InfoNode(name, top.depth + 1);
87                        top.add(n);
88                        top = n;
89                        open.add(top);
90                        top.begin();
91                }
92               
93                public void end(String name) {
94                        if (name == null)
95                                throw new IllegalArgumentException("Must give a name");
96                        if (!top.name.equals(name))
97                                throw new IllegalArgumentException("Name does not match last opened step: " + 
98                                                name + ", last: " + top.name);
99                        top.end();
100                        open.remove(open.size() - 1);
101                        top = open.get(open.size() -1);
102                }
103               
104                public void logReport() {
105                        logReport(Integer.MAX_VALUE);
106                }
107               
108                public void logReport(int maxDepth) {
109                        while (top.depth > 0) 
110                                end(top.name);
111                        top.end();
112                       
113                        ArrayList<String[]> lines = new ArrayList<String[]>();
114                        lines.add(top.toStrings());
115                        for (InfoNode n : top)
116                                if (n.depth <= maxDepth)
117                                        lines.add(n.toStrings());
118                       
119                        int n = lines.get(0).length;
120                        int[] lengths = new int[n];
121                        for (String[] line : lines)
122                                for (int i = 0; i < n; i++)
123                                        if (line[i].length() > lengths[i])
124                                                lengths[i] = line[i].length();
125                       
126                        StringBuilder buf = new StringBuilder("%-");
127                        buf.append(lengths[0]);
128                        buf.append("s: ");
129                        for (int i = 1; i < n; i++) {
130                                buf.append("%");
131                                buf.append(lengths[i]);
132                                buf.append((i < n - 1) ? "s, " : "s");
133                        }
134                        String fmt = buf.toString();
135                       
136                        for (String[] line : lines) 
137                                ASTNode.log.debug(String.format(fmt, (Object[]) line));
138                }
139               
140                public String[][] rawLog() {
141                        while (top.depth > 0) 
142                                end(top.name);
143                        top.end();
144                       
145                        java.util.List<String[]> lines = new ArrayList<String[]>();
146                        lines.add(top.toStringsRaw());
147                        for (InfoNode n : top)
148                                lines.add(n.toStringsRaw());
149                       
150                        return lines.toArray(new String[lines.size()][]);
151                }
152                private static final String[][] measurmentNames = new String[][] {new String[]{"type"}, new String[]{"time"}, new String[]{"memoryDiff", "memoryTotal"}};
153               
154                public void writeCSVFile(File file) throws FileNotFoundException {
155                        String[][] rawMemLog = rawLog();
156                        String[][][] memLog = new String[rawMemLog.length][][];
157                        for (int i = 0; i < rawMemLog.length; i++) {
158                                memLog[i] = new String[rawMemLog[i].length][];
159                                for (int j = 0; j < rawMemLog[i].length; j++) {
160                                        if (j == 0)
161                                                memLog[i][j] = new String[] {rawMemLog[i][j]};
162                                        else
163                                                memLog[i][j] = rawMemLog[i][j].split(",");
164                                }
165                        }
166                        PrintStream memOutFile = new PrintStream(file);
167                        for (int j = 0; j < memLog[0].length; j++) {
168                                for (int k = 0; k < memLog[0][j].length; k++) {
169                                        memOutFile.print("\"");
170                                        memOutFile.print(measurmentNames[j][k]);
171                                        memOutFile.print("\"");
172                                        for (int i = 0; i < memLog.length; i++) {
173                                                memOutFile.print(",\"");
174                                                memOutFile.print(memLog[i][j][k]);
175                                                memOutFile.print('"');
176                                        }
177                                        memOutFile.println();
178                                }
179                        }
180                        memOutFile.close();
181                }
182               
183                private InfoItem[] createItems() {
184                        InfoItem[] res = new InfoItem[itemClasses.size()];
185                        Class x = null;
186                        for (int i = 0; i < res.length; i++) {
187                                try {
188                                        res[i] = itemClasses.get(i).newInstance();
189                                } catch (Exception e) {
190                                        throw new IllegalArgumentException("Could not instantiate info item", e);
191                                }
192                        }
193                        return res;
194                }
195               
196                private class InfoNode implements Iterable<InfoNode> {
197                       
198                        private static final char INDENT = ' ';
199                       
200                        private InfoItem[] items;
201                        private ArrayList<InfoNode> children;
202                        public String name;
203                        public int depth;
204                       
205                        public InfoNode(String name, int depth) {
206                                this.name = name;
207                                this.depth = depth;
208                                items = createItems();
209                                children = null;
210                        }
211                       
212                        public void begin() {
213                                for (InfoItem it : items)
214                                        it.begin();
215                        }
216                       
217                        public void end() {
218                                for (InfoItem it : items)
219                                        it.end();
220                        }
221                       
222                        public void add(InfoNode n) {
223                                if (children == null)
224                                        children = new ArrayList<InfoNode>();
225                                children.add(n);
226                        }
227                       
228                        private String produceNameHead() {
229                                StringBuilder buf = new StringBuilder();
230                                for (int j = 0; j < depth; j++)
231                                        buf.append(INDENT);
232                                buf.append(name);
233                                return buf.toString();
234                        }
235                       
236                        public String[] toStrings() {
237                                String[] res = new String[items.length + 1];
238                                res[0] = produceNameHead();
239                                int i = 1;
240                                for (InfoItem it : items)
241                                        res[i++] = it.toString();
242                                return res;
243                        }
244                       
245                        public String[] toStringsRaw() {
246                                String[] res = new String[items.length + 1];
247                                res[0] = produceNameHead();
248                                int i = 1;
249                                for (InfoItem it : items)
250                                        res[i++] = it.toStringRaw();
251                                return res;
252                        }
253                       
254                        public Iterator<InfoNode> iterator() {
255                                return new NodeIterator();
256                        }
257                       
258                        private class NodeIterator implements Iterator<InfoNode> {
259                               
260                                int i = 0;
261                                Iterator<InfoNode> cur = null;
262                               
263                                public boolean hasNext() {
264                                        return children != null && i < children.size();
265                                }
266                               
267                                public InfoNode next() {
268                                        InfoNode res;
269                                        if (cur != null) {
270                                                res = cur.next();
271                                        } else {
272                                                res = children.get(i);
273                                                cur = res.iterator();
274                                        }
275                                        if (!cur.hasNext()) {
276                                                cur = null;
277                                                i++;
278                                        }
279                                        return res;
280                                }
281                               
282                                public void remove() {
283                                        throw new UnsupportedOperationException();
284                                }
285                               
286                        }
287                       
288                }
289               
290                public interface InfoItem {
291                        public void begin();
292                        public void end();
293                        public String toString();
294                        public String toStringRaw();
295                }
296               
297                public static abstract class StateDifferenceInfoItem implements InfoItem {
298                        private long beginVal = 0;
299                        private long endVal = 0;
300                       
301                        public void begin() {
302                                beginVal = state();
303                        }
304                       
305                        public void end() {
306                                endVal = state();
307                        }
308                       
309                        public String toString() {
310                                return toString(endVal - beginVal, endVal);
311                        }
312                       
313                        public String toStringRaw() {
314                                return toStringRaw(endVal - beginVal, endVal);
315                        }
316                       
317                        public abstract long state();
318                        public abstract String toString(long diff, long endVal);
319                        public abstract String toStringRaw(long diff, long endVal);
320                }
321               
322                public static class TimeItem extends StateDifferenceInfoItem {
323                        public long state() {
324                                return System.currentTimeMillis() - MemoryChangeItem.timeSpentInGC;
325                        }
326                       
327                        public String toString(long diff, long endVal) {
328                                return (Math.round(diff / 10.0) / 100.0) + " s";
329                        }
330                       
331                        public String toStringRaw(long diff, long endVal) {
332                                return Long.toString(diff);
333                        }
334                }
335               
336                public static class MemoryChangeItem extends StateDifferenceInfoItem {
337                        private static final Runtime RUNTIME = Runtime.getRuntime();
338                        private static long timeSpentInGC = 0;
339                       
340                        public static long getTimeSpentInGC() {
341                                return timeSpentInGC;
342                        }
343
344                        public long state() {
345                                if (StepInfo.GC_BEFORE_MEM)
346                                        runGC();
347                                return usedMemory();
348                        }
349                       
350                        private static long usedMemory() {
351                                return RUNTIME.totalMemory() - RUNTIME.freeMemory();
352                        }
353                       
354                        private static void runGC() {
355                                long start = System.currentTimeMillis();
356                                try {
357                                        long usedMem1 = usedMemory(), usedMem2 = Long.MAX_VALUE;
358                                        for (int i = 0; (usedMem1 < usedMem2) && (i < 500); ++i) {
359                                                RUNTIME.runFinalization();
360                                                RUNTIME.gc();
361                                                Thread.yield();
362       
363                                                usedMem2 = usedMem1;
364                                                usedMem1 = usedMemory();
365                                        }
366                                } catch (Exception e) {}
367                                timeSpentInGC += System.currentTimeMillis() - start;
368                        }
369                       
370                        public String toString(long diff, long endVal) {
371                                return ASTNode.formatMem(diff) + " (" + ASTNode.formatMem(endVal) + " total)";
372                        }
373                       
374                        public String toStringRaw(long diff, long endVal) {
375                                return diff + "," + endVal;
376                        }
377                }
378               
379        }
380       
381}
382
383aspect MemoryUse {
384
385    public Iterable<ASTNode> ASTNode.profilingChildren() {
386        LinkedHashSet<ASTNode> res = new LinkedHashSet<ASTNode>(children != null ? children.length * 2 : 4);
387        if (children != null)
388            for (ASTNode ch : children)
389                if (ch != null && ch.parent == this)
390                    res.add(ch);
391        for (Field f : Profiler.GET_FIELDS.perform(getClass())) {
392            if ((f.getModifiers() & Modifier.STATIC) == 0 && ASTNode.class.isAssignableFrom(f.getType())) {
393                try {
394                    ASTNode val = (ASTNode) Profiler.GET_VALUE.perform(f, this);
395                    if (val != null && val.parent == this)
396                        res.add(val);
397                } catch (Exception e) {}
398            }
399        }
400        return res;
401    }
402
403    private Map<Class,Counter> ASTNode.buildNodeCountMap = null;
404
405    private static NodeCountExtra[] ASTNode.nodeCountExtra = new NodeCountExtra[] { };
406
407    public interface NodeCountExtra {
408        public void count(ASTNode n);
409        public void printResult(CodeStream out);
410    }
411
412    /**
413     * Output a list of the number of instances of each node class.
414     */
415    public void ASTNode.dumpNodeCount(String file)
416            throws IOException {
417        ArrayList<Counter> list = new ArrayList<Counter>();
418        list.addAll(buildNodeCountMap.values());
419        Collections.sort(list);
420        CodeStream out = new CodeStream(file);
421        for (Counter c : list)
422            out.println(c);
423        for (NodeCountExtra nce : nodeCountExtra)
424            nce.printResult(out);
425        out.close();
426    }
427
428    public void ASTNode.buildNodeCount() {
429        Profiler.clear();
430        if (buildNodeCountMap == null)
431            buildNodeCountMap = new HashMap<Class,Counter>();
432        buildNodeCount(buildNodeCountMap);
433        Profiler.clear();
434    }
435
436    private void ASTNode.buildNodeCount(Map<Class,Counter> map) {
437        addToNodeCount(map, getClass(), Profiler.getNodeSize(this, false), Profiler.getNodeSize(this, true));
438        for (NodeCountExtra nce : nodeCountExtra)
439            nce.count(this);
440       
441        for (ASTNode n : profilingChildren())
442            n.buildNodeCount(map);
443    }
444
445    private void ASTNode.addToNodeCount(Map<Class,Counter> map, Class cls, long local, long deep) {
446        if (map.containsKey(cls))
447            map.get(cls).inc(local, deep);
448        else
449            map.put(cls, new Counter(cls.getSimpleName(), local, deep));
450        if (cls != ASTNode.class)
451            addToNodeCount(map, cls.getSuperclass(), local, deep);
452    }
453
454    public class ASTNode {
455        public static class Counter implements Comparable<Counter> {
456            private int n;
457            private long sizeLocal;
458            private long sizeDeep;
459            private String s;
460           
461            private static int l = 0;
462            private static String fmt = null;
463           
464            public Counter(String name, long local, long deep) {
465                s = name;
466                sizeLocal = 0;
467                sizeDeep = 0;
468                n = 0;
469                if (s.length() > l)
470                    l = s.length();
471                inc(local, deep);
472            }
473           
474            public void inc(long local, long deep) {
475                n++;
476                sizeLocal += local;
477                sizeDeep += deep;
478            }
479           
480            public int compareTo(Counter c) {
481                return c.n - n;
482            }
483           
484            public String toString() {
485                if (fmt == null)
486                    fmt = "%-" + l + "s : %9d (%8s / %8s)";
487                return String.format(fmt, s, n, ASTNode.formatMem(sizeDeep), ASTNode.formatMem(sizeLocal));
488            }
489        }
490    }
491
492        /**
493         * Output a view of an AST, showing the classname and approximate memory footprint
494         *        of the subtree.
495         *
496         * As {@link #dumpMemoryUse(PrintStream, boolean, int, long)}, with <code>deep = false</code>,
497         * <code>maxDepth = -1</code>, <code>minSize = 0</code>, and saving the output to a file.
498         *
499         * @param file     filename to save output as
500         */
501        public void ASTNode.dumpMemoryUse(String file) throws FileNotFoundException {
502                dumpMemoryUse(file, false);
503        }
504       
505        /**
506         * Output a view of an AST, showing the classname and approximate memory footprint
507         *        of the subtree.
508         *
509         * As {@link #dumpMemoryUse(PrintStream, boolean, int, long)}, with <code>maxDepth = -1</code>,
510         * <code>minSize = 0</code>, and saving the output to a file.
511         *
512         * @param file     filename to save output as
513         * @param deep     if the memory calculation should include the contents of non-ASTNode members
514         */
515        public void ASTNode.dumpMemoryUse(String file, boolean deep) throws FileNotFoundException {
516                dumpMemoryUse(file, deep, -1, 0);
517        }
518       
519        /**
520         * Output a view of an AST, showing the classname and approximate memory footprint
521         *        of the subtree.
522         *
523         * As {@link #dumpMemoryUse(PrintStream, boolean, int, long)}, but saving the output to a file.
524         *
525         * @param file     filename to save output as
526         * @param deep     if the memory calculation should include the contents of non-ASTNode members
527         * @param maxDepth the maximum depth to display nodes from, -1 means infinite depth
528         * @param minSize  the minimum memory size to display a node
529         */
530        public void ASTNode.dumpMemoryUse(String file, boolean deep, int maxDepth, long minSize) 
531                        throws FileNotFoundException {
532                dumpMemoryUse(new PrintStream(file), deep, maxDepth, minSize);
533        }
534       
535       
536        /**
537         * Output a view of an AST, showing the classname and approximate memory footprint
538         *        of the subtree.
539         *
540         * As {@link #dumpMemoryUse(PrintStream, boolean, int, long)}, with <code>deep = false</code>,
541         * <code>maxDepth = -1</code> and <code>minSize = 0</code>.
542         *
543         * @param out      stream to use for output
544         */
545        public void ASTNode.dumpMemoryUse(PrintStream out) {
546                dumpMemoryUse(out, false, -1, 0);
547        }
548       
549        /**
550         * Output a view of an AST, showing the classname and approximate memory footprint
551         *        of the subtree.
552         *
553         * As {@link #dumpMemoryUse(PrintStream, boolean, int, long)}, with <code>maxDepth = -1</code> and
554         * <code>minSize = 0</code>.
555         *
556         * @param out      stream to use for output
557         * @param deep     if the memory calculation should include the contents of non-ASTNode members
558         */
559        public void ASTNode.dumpMemoryUse(PrintStream out, boolean deep) {
560                dumpMemoryUse(out, deep, -1, 0);
561        }
562       
563        /**
564         * Output a view of an AST, showing the classname and approximate memory footprint
565         *        of the subtree.
566         *
567         * @param out      stream to use for output
568         * @param deep     if the memory calculation should include the contents of non-ASTNode members
569         * @param maxDepth the maximum depth to display nodes from, -1 means infinite depth
570         * @param minSize  the minimum memory size to display a node
571         */
572        public void ASTNode.dumpMemoryUse(PrintStream out, boolean deep, int maxDepth, long minSize) {
573                Profiler.clear();   // Remove any stale data from profiler
574                gatherMemoryUse(out, "", deep, maxDepth, minSize, 0);
575                Profiler.clear();   // Free any memory used by the profiler's cache
576        }
577       
578        /**
579         * Calculate the memory weight of a subtree.
580         */
581        public long ASTNode.subTreeSize() {
582                Profiler.clear();   // Remove any stale data from profiler
583                long res = subTreeSizeNoClear();
584                Profiler.clear();   // Free any memory used by the profiler's cache
585                return res;
586        }
587       
588        /**
589         * Calculate the memory weight of a subtree, without clearing cache.
590         */
591        private long ASTNode.subTreeSizeNoClear() {
592                return gatherMemoryUse(NullStream.PRINT, "", true, 0, 0, 1);
593        }
594       
595        /**
596         * Calclulate memory weight of all subtrees fulfilling a specific criteria.
597         */
598        public long ASTNode.filteredTreeSize(Criteria<ASTNode> filter) {
599                Profiler.clear();   // Remove any stale data from profiler
600                long res = filteredTreeSizeNoClear(filter);
601                Profiler.clear();   // Free any memory used by the profiler's cache
602                return res;
603        }
604
605    /**
606     * Calclulate memory weight of all subtrees fulfilling a specific criteria, without clearing cache.
607     */
608    private long ASTNode.filteredTreeSizeNoClear(Criteria<ASTNode> filter) {
609        if (filter.test(this)) {
610            return subTreeSizeNoClear();
611        } else {
612            long res = 0;
613            for (ASTNode ch : profilingChildren()) 
614                res += ch.filteredTreeSizeNoClear(filter);
615            return res;
616        }
617    }
618
619        /**
620         * Calculate total memory weight of all nodes (including subtrees) of a specific class.
621         */
622        public long ASTNode.specificNodeClassTreeSize(String name) {
623                try {
624                        final Class cls = Class.forName(ASTNode.class.getName().replace("ASTNode", name));
625                        return filteredTreeSize(new ClassCriteria<ASTNode>(cls));
626                } catch (ClassNotFoundException e) {
627                        return -1;
628                }
629        }
630
631    /**
632     * Traversal method for {@link #dumpMemoryUse(PrintStream, boolean, int, long)}.
633     *
634     * @return approximation of the memory footprint for the subtree
635     */
636    protected long ASTNode.gatherMemoryUse(
637            PrintStream out, String indent, boolean deep, int maxDepth, long minSize, int depth) {
638        long local = Profiler.getNodeSize(this, deep);
639        long mem = local;
640        String nextInd = indent + " ";
641        for (ASTNode ch : profilingChildren())
642            mem += ch.gatherMemoryUse(out, nextInd, deep, maxDepth, minSize, depth+1);
643        if ((depth == 0 || mem >= minSize) && (maxDepth < 0 || depth < maxDepth))
644            addMemoryUseRow(out, indent, mem, local);
645        return mem;
646    }
647
648        protected long Opt.gatherMemoryUse(PrintStream out, String indent, boolean deep, int maxDepth, long minSize, int depth) {
649                return super.gatherMemoryUse(out, indent.substring(1), deep, maxDepth, minSize, depth);
650        }
651       
652        protected long InstExtends.gatherMemoryUse(PrintStream out, String indent, boolean deep, int maxDepth, long minSize, int depth) {
653                return super.gatherMemoryUse(out, indent, deep, maxDepth, minSize, depth);
654        }
655
656        /**
657         * Output method for {@link #dumpMemoryUse(PrintStream, boolean, int, long)}.
658         */
659        protected void ASTNode.addMemoryUseRow(PrintStream out, String indent, long mem, long local) {
660                out.println(indent + getClass().getSimpleName() + extraMemoryUseInfo() + ": " + 
661                                formatMem(mem) + " (" + formatMem(local) + ")");
662        }
663       
664        protected void Opt.addMemoryUseRow(PrintStream out, String indent, long mem, long local) {}
665       
666        /**
667         * Any extra info to add to the memory use output.
668         */
669        syn String ASTNode.extraMemoryUseInfo()   = "";
670        eq InstClassDecl.extraMemoryUseInfo()     = " \"" + name() + "\"";
671        eq InstComponentDecl.extraMemoryUseInfo() = " \"" + name() + "\"";
672        eq SrcClassDecl.extraMemoryUseInfo()         = " \"" + name() + "\"";
673        eq SrcComponentDecl.extraMemoryUseInfo()     = " \"" + name() + "\"";
674        eq InstExtends.extraMemoryUseInfo()       = " extending \"" + getClassName().name() + "\"";
675
676    /**
677     * Visit each node in tree (depth-first).
678     */
679    public void ASTNode.visitAll(ASTVisitor v) {
680        for (ASTNode ch : profilingChildren())
681            ch.visitAll(v);
682        v.visit(this);
683    }
684
685        /**
686         * Template for vistor object for tree.
687         */
688        public interface ASTVisitor {
689                public void visit(ASTNode n);
690        }
691       
692        /**
693         * Base class for ASTVisitor filtered by a criteria.
694         */
695        public abstract class FilteredASTVisitor implements ASTVisitor {
696                private Criteria<ASTNode> crit;
697               
698                public FilteredASTVisitor(Criteria<ASTNode> crit) {
699                        this.crit = crit;
700                }
701               
702                public void visit(ASTNode n) {
703                        if (crit.test(n))
704                                filteredVisit(n);
705                }
706               
707                public abstract void filteredVisit(ASTNode n);
708        }
709       
710        /**
711         * A criteria that checks that the object is an instance of a specific class.
712         */
713        public class ClassCriteria<T> implements Criteria<T> {
714                private Class cls;
715               
716                public ClassCriteria(Class cls) {
717                        this.cls = cls;
718                }
719               
720                public boolean test(T elem) {
721                        return cls.isInstance(elem);
722                }
723        }
724
725    /**
726     * Contains methods for calculating the size of AST nodes.
727     *
728     * Uses minimum sizes stipulated by language standard, and ignores padding for memory alignment
729     * used by many JVMs. Thus values should be treated as minimum values.
730     */
731    public abstract class Profiler {
732
733        /**
734         * Approximates the memory footprint of an AST node.
735         *
736         * @param deep  if the approximation should include the contents of non-ASTNode members
737         */
738        public static long getNodeSize(ASTNode node, boolean deep) {
739            if (deep)
740                return getObjectSize(node);
741            else 
742                return getTotalShellSize(node.getClass());
743        }
744
745        /**
746         * Clear cached data.
747         */
748        public static void clear() {
749            visited.clear();
750        }
751
752        private static final long OBJECT_PAD_SIZE     = 8;
753        private static final long OBJECT_SHELL_SIZE   = 12;
754        private static final long OBJREF_SIZE         = 4;
755        private static final long LONG_FIELD_SIZE     = 8;
756        private static final long INT_FIELD_SIZE      = 4;
757        private static final long SHORT_FIELD_SIZE    = 2;
758        private static final long CHAR_FIELD_SIZE     = 2;
759        private static final long BYTE_FIELD_SIZE     = 1;
760        private static final long BOOLEAN_FIELD_SIZE  = 1;
761        private static final long DOUBLE_FIELD_SIZE   = 8;
762        private static final long FLOAT_FIELD_SIZE    = 4;
763
764        private static HashMap<Class, Long> totalFieldSize = new HashMap<Class, Long>();
765        private static IdentityHashMap<Object, Object> visited = new IdentityHashMap<Object, Object>();
766
767        public static class GetFieldsAction implements PrivilegedAction {
768           
769            private Class cls;
770           
771            public Field[] perform(Class cl) {
772                cls = cl;
773                return (Field[]) AccessController.doPrivileged(this);
774            }
775
776            public Object run() {
777                return cls.getDeclaredFields();
778            }
779           
780        }
781
782        public static class GetValueAction implements PrivilegedExceptionAction {
783           
784            private Field field;
785           
786            public Object perform(Field f, Object o) throws Exception {
787                if (!f.isAccessible()) {
788                    field = f;
789                    AccessController.doPrivileged(this);
790                }
791                return f.get(o);
792            }
793
794            public Object run() throws Exception {
795                field.setAccessible(true);
796                return null;
797            }
798           
799        }
800
801        public static final GetFieldsAction GET_FIELDS = new GetFieldsAction();
802        public static final GetValueAction  GET_VALUE  = new GetValueAction();
803
804        public static long getObjectSize(Object o) {
805            if (o == null || visited.containsKey(o))
806                return 0;
807            visited.put(o, null);
808           
809            Class type = o.getClass();
810            if (type.isArray())
811                return getArraySize(o, type);
812            if (o instanceof LinkedList)
813                return getLinkedListSize((LinkedList) o, type);
814           
815            long mem = getTotalShellSize(type);
816            for (; type != null; type = type.getSuperclass())
817                for (Field f : GET_FIELDS.perform(type)) 
818                    if ((f.getModifiers() & Modifier.STATIC) == 0) 
819                        mem += getObjectFieldSize(f, o);
820            return mem;
821        }
822
823        public static void printObjectParts(Object o) {
824            printObjectParts(o, o.getClass());
825        }
826
827        private static void printObjectParts(Object o, Class type) {
828            if (type != Object.class)
829                printObjectParts(o, type.getSuperclass());
830           
831            for (Field f : GET_FIELDS.perform(type)) {
832                if ((f.getModifiers() & Modifier.STATIC) == 0) {
833                    long mem = getFieldSize(f.getType()) + getObjectFieldSize(f, o);
834                    System.out.format("%6d : %s\n", mem, f.getName());
835                }
836            }
837        }
838
839        private static long getObjectFieldSize(Field f, Object o) {
840            Class type = f.getType();
841            if (type.isPrimitive())
842                return 0;
843            try {
844                Object val = GET_VALUE.perform(f, o);
845                return shouldCount(val) ? getObjectSize(val) : 0;
846            } catch (Exception e) {
847                System.err.println("Could not read member: " + o.getClass().getSimpleName() + "." + f.getName());
848                return OBJECT_SHELL_SIZE;
849            }
850        }
851
852        private static boolean shouldCount(Object o) {
853            if (o instanceof ASTNode) {
854                ASTNode n = (ASTNode) o;
855                for (; n.parent != null; n = n.parent)
856                    if (!n.recognizedByParent())
857                        return true;
858                return !(n instanceof Root);
859            } else {
860                return true;
861            }
862        }
863
864        // Special case for linked list to avoid stack overflow
865        private static long getLinkedListSize(LinkedList l, Class type) {
866            long sum = getTotalShellSize(type);
867            long entrySize;
868            try {
869                entrySize = getTotalShellSize(type.getField("header").getType());
870            } catch (Exception e) {
871                entrySize = 3 * OBJREF_SIZE + OBJECT_SHELL_SIZE; // From inspecting source
872            }
873            for (Object o : l)
874                sum += entrySize + getObjectSize(o);
875            return sum;
876        }
877
878        private static long getTotalFieldSize(Class type) {
879            if (totalFieldSize.containsKey(type))
880                return totalFieldSize.get(type);
881           
882            long mem = 0;
883            if (type != Object.class)
884                mem = getTotalFieldSize(type.getSuperclass());
885           
886            for (Field f : GET_FIELDS.perform(type)) 
887                if ((f.getModifiers() & Modifier.STATIC) == 0) 
888                    mem += getFieldSize(f.getType());
889           
890            totalFieldSize.put(type, mem);
891            return mem;
892        }
893
894        public static long getTotalShellSize(Class type) {
895            long mem = OBJECT_SHELL_SIZE + getTotalFieldSize(type);
896            return ((mem - 1) / OBJECT_PAD_SIZE + 1) * OBJECT_PAD_SIZE;
897        }
898
899        private static long getFieldSize(Class type) {
900            if (type == int.class)
901                return INT_FIELD_SIZE;
902            else if (type == long.class)
903                return LONG_FIELD_SIZE;
904            else if (type == short.class)
905                return SHORT_FIELD_SIZE;
906            else if (type == byte.class)
907                return BYTE_FIELD_SIZE;
908            else if (type == boolean.class)
909                return BOOLEAN_FIELD_SIZE;
910            else if (type == char.class)
911                return CHAR_FIELD_SIZE;
912            else if (type == double.class)
913                return DOUBLE_FIELD_SIZE;
914            else if (type == float.class)
915                return FLOAT_FIELD_SIZE;
916            return OBJREF_SIZE;
917        }
918
919        private static long getArraySize(Object o, Class type) {
920            int len = java.lang.reflect.Array.getLength(o);
921            Class comp = type.getComponentType();
922            long size = getFieldSize(comp);
923            long res = OBJECT_SHELL_SIZE + INT_FIELD_SIZE + len * size;
924            res = ((res - 1) / OBJECT_PAD_SIZE + 1) * OBJECT_PAD_SIZE;
925            if (!comp.isPrimitive()) { 
926                for (int i = 0; i < len; i++) {
927                    Object elem = java.lang.reflect.Array.get(o, i);
928                    if (shouldCount(elem))
929                        res += getObjectSize(elem);
930                }
931            }
932            return res;
933        }
934
935    }
936
937        public boolean ASTNode.recognizedByParent() {
938                return parent != null && 
939                                parent.children != null && 
940                                childIndex >= 0 && childIndex < parent.children.length &&
941                                parent.children[childIndex] == this;
942        }
943
944}
Note: See TracBrowser for help on using the repository browser.