source: trunk/Compiler/ModelicaMiddleEnd/src/jastadd/Profiling.jrag @ 12940

Last change on this file since 12940 was 12940, checked in by tgutzmann, 6 months ago

#5788 Addressed many warnings. Most noteworthy changes:

  • removed "ChapterList" which was unused and had a serious implementation flaw, as identified by a warning
  • switched to try-with-resources everywhere
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 static 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 static 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.