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

Last change on this file since 13998 was 13998, checked in by jwedin, 4 weeks ago

Removed the unused interface NodeCountExtra. Updated the documentation for the node count functionality. #5865

File size: 28.8 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    /**
404     * Output a list of the number of instances of each node class.
405     */
406    public void ASTNode.dumpNodeCount(String file, UtilInterface util)
407            throws IOException {
408        ArrayList<ASTNodeCounter> list = new ArrayList<ASTNodeCounter>();
409        list.addAll(util.getBuildNodeCountMap().values());
410        Collections.sort(list);
411        CodeStream out = new CodeStream(file);
412        for (ASTNodeCounter c : list)
413            out.println(c);
414        out.close();
415    }
416
417    public void ASTNode.buildNodeCount(Map<Class,ASTNodeCounter> map) {
418        addToNodeCount(map, getClass(), Profiler.getNodeSize(this, false), Profiler.getNodeSize(this, true));
419       
420        for (ASTNode n : profilingChildren())
421            n.buildNodeCount(map);
422    }
423
424    private void ASTNode.addToNodeCount(Map<Class,ASTNodeCounter> map, Class cls, long local, long deep) {
425        if (map.containsKey(cls))
426            map.get(cls).inc(local, deep);
427        else
428            map.put(cls, new ASTNodeCounter(cls.getSimpleName(), local, deep));
429        if (cls != ASTNode.class)
430            addToNodeCount(map, cls.getSuperclass(), local, deep);
431    }
432
433    public class ASTNodeCounter implements Comparable<ASTNodeCounter> {
434        private int n;
435        private long sizeLocal;
436        private long sizeDeep;
437        private String s;
438       
439        private static int l = 0;
440        private static String fmt = null;
441       
442        public ASTNodeCounter(String name, long local, long deep) {
443            s = name;
444            sizeLocal = 0;
445            sizeDeep = 0;
446            n = 0;
447            if (s.length() > l)
448                l = s.length();
449            inc(local, deep);
450        }
451       
452        public void inc(long local, long deep) {
453            n++;
454            sizeLocal += local;
455            sizeDeep += deep;
456        }
457       
458        public int compareTo(ASTNodeCounter c) {
459            return c.n - n;
460        }
461       
462        public String toString() {
463            if (fmt == null)
464                fmt = "%-" + l + "s : %9d (%8s / %8s)";
465            return String.format(fmt, s, n, ASTNode.formatMem(sizeDeep), ASTNode.formatMem(sizeLocal));
466        }
467    }
468
469    public class UtilInterface {
470        private Map<Class, ASTNodeCounter> buildNodeCountMap;
471        public Map<Class, ASTNodeCounter> getBuildNodeCountMap() {
472            if (buildNodeCountMap == null) {
473                buildNodeCountMap = new HashMap<>();
474            }
475            return buildNodeCountMap;
476        }
477    }
478
479        /**
480         * Output a view of an AST, showing the classname and approximate memory footprint
481         *        of the subtree.
482         *
483         * As {@link #dumpMemoryUse(PrintStream, boolean, int, long)}, with <code>deep = false</code>,
484         * <code>maxDepth = -1</code>, <code>minSize = 0</code>, and saving the output to a file.
485         *
486         * @param file     filename to save output as
487         */
488        public void ASTNode.dumpMemoryUse(String file) throws FileNotFoundException {
489                dumpMemoryUse(file, false);
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>maxDepth = -1</code>,
497         * <code>minSize = 0</code>, and saving the output to a file.
498         *
499         * @param file     filename to save output as
500         * @param deep     if the memory calculation should include the contents of non-ASTNode members
501         */
502        public void ASTNode.dumpMemoryUse(String file, boolean deep) throws FileNotFoundException {
503                dumpMemoryUse(file, deep, -1, 0);
504        }
505       
506        /**
507         * Output a view of an AST, showing the classname and approximate memory footprint
508         *        of the subtree.
509         *
510         * As {@link #dumpMemoryUse(PrintStream, boolean, int, long)}, but 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         * @param maxDepth the maximum depth to display nodes from, -1 means infinite depth
515         * @param minSize  the minimum memory size to display a node
516         */
517        public void ASTNode.dumpMemoryUse(String file, boolean deep, int maxDepth, long minSize) 
518                        throws FileNotFoundException {
519                dumpMemoryUse(new PrintStream(file), deep, maxDepth, minSize);
520        }
521       
522       
523        /**
524         * Output a view of an AST, showing the classname and approximate memory footprint
525         *        of the subtree.
526         *
527         * As {@link #dumpMemoryUse(PrintStream, boolean, int, long)}, with <code>deep = false</code>,
528         * <code>maxDepth = -1</code> and <code>minSize = 0</code>.
529         *
530         * @param out      stream to use for output
531         */
532        public void ASTNode.dumpMemoryUse(PrintStream out) {
533                dumpMemoryUse(out, false, -1, 0);
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>maxDepth = -1</code> and
541         * <code>minSize = 0</code>.
542         *
543         * @param out      stream to use for output
544         * @param deep     if the memory calculation should include the contents of non-ASTNode members
545         */
546        public void ASTNode.dumpMemoryUse(PrintStream out, boolean deep) {
547                dumpMemoryUse(out, deep, -1, 0);
548        }
549       
550        /**
551         * Output a view of an AST, showing the classname and approximate memory footprint
552         *        of the subtree.
553         *
554         * @param out      stream to use for output
555         * @param deep     if the memory calculation should include the contents of non-ASTNode members
556         * @param maxDepth the maximum depth to display nodes from, -1 means infinite depth
557         * @param minSize  the minimum memory size to display a node
558         */
559        public void ASTNode.dumpMemoryUse(PrintStream out, boolean deep, int maxDepth, long minSize) {
560                Profiler.clear();   // Remove any stale data from profiler
561                gatherMemoryUse(out, "", deep, maxDepth, minSize, 0);
562                Profiler.clear();   // Free any memory used by the profiler's cache
563        }
564       
565        /**
566         * Calculate the memory weight of a subtree.
567         */
568        public long ASTNode.subTreeSize() {
569                Profiler.clear();   // Remove any stale data from profiler
570                long res = subTreeSizeNoClear();
571                Profiler.clear();   // Free any memory used by the profiler's cache
572                return res;
573        }
574       
575        /**
576         * Calculate the memory weight of a subtree, without clearing cache.
577         */
578        private long ASTNode.subTreeSizeNoClear() {
579                return gatherMemoryUse(NullStream.PRINT, "", true, 0, 0, 1);
580        }
581       
582        /**
583         * Calclulate memory weight of all subtrees fulfilling a specific criteria.
584         */
585        public long ASTNode.filteredTreeSize(Criteria<ASTNode> filter) {
586                Profiler.clear();   // Remove any stale data from profiler
587                long res = filteredTreeSizeNoClear(filter);
588                Profiler.clear();   // Free any memory used by the profiler's cache
589                return res;
590        }
591
592    /**
593     * Calclulate memory weight of all subtrees fulfilling a specific criteria, without clearing cache.
594     */
595    private long ASTNode.filteredTreeSizeNoClear(Criteria<ASTNode> filter) {
596        if (filter.test(this)) {
597            return subTreeSizeNoClear();
598        } else {
599            long res = 0;
600            for (ASTNode ch : profilingChildren()) 
601                res += ch.filteredTreeSizeNoClear(filter);
602            return res;
603        }
604    }
605
606        /**
607         * Calculate total memory weight of all nodes (including subtrees) of a specific class.
608         */
609        public long ASTNode.specificNodeClassTreeSize(String name) {
610                try {
611                        final Class cls = Class.forName(ASTNode.class.getName().replace("ASTNode", name));
612                        return filteredTreeSize(new ClassCriteria<ASTNode>(cls));
613                } catch (ClassNotFoundException e) {
614                        return -1;
615                }
616        }
617
618    /**
619     * Traversal method for {@link #dumpMemoryUse(PrintStream, boolean, int, long)}.
620     *
621     * @return approximation of the memory footprint for the subtree
622     */
623    protected long ASTNode.gatherMemoryUse(
624            PrintStream out, String indent, boolean deep, int maxDepth, long minSize, int depth) {
625        long local = Profiler.getNodeSize(this, deep);
626        long mem = local;
627        String nextInd = indent + " ";
628        for (ASTNode ch : profilingChildren())
629            mem += ch.gatherMemoryUse(out, nextInd, deep, maxDepth, minSize, depth+1);
630        if ((depth == 0 || mem >= minSize) && (maxDepth < 0 || depth < maxDepth))
631            addMemoryUseRow(out, indent, mem, local);
632        return mem;
633    }
634
635        protected long Opt.gatherMemoryUse(PrintStream out, String indent, boolean deep, int maxDepth, long minSize, int depth) {
636                return super.gatherMemoryUse(out, indent.substring(1), deep, maxDepth, minSize, depth);
637        }
638       
639        protected long InstExtends.gatherMemoryUse(PrintStream out, String indent, boolean deep, int maxDepth, long minSize, int depth) {
640                return super.gatherMemoryUse(out, indent, deep, maxDepth, minSize, depth);
641        }
642
643        /**
644         * Output method for {@link #dumpMemoryUse(PrintStream, boolean, int, long)}.
645         */
646        protected void ASTNode.addMemoryUseRow(PrintStream out, String indent, long mem, long local) {
647                out.println(indent + getClass().getSimpleName() + extraMemoryUseInfo() + ": " + 
648                                formatMem(mem) + " (" + formatMem(local) + ")");
649        }
650       
651        protected void Opt.addMemoryUseRow(PrintStream out, String indent, long mem, long local) {}
652       
653        /**
654         * Any extra info to add to the memory use output.
655         */
656        syn String ASTNode.extraMemoryUseInfo()   = "";
657        eq InstClassDecl.extraMemoryUseInfo()     = " \"" + name() + "\"";
658        eq InstComponentDecl.extraMemoryUseInfo() = " \"" + name() + "\"";
659        eq SrcClassDecl.extraMemoryUseInfo()         = " \"" + name() + "\"";
660        eq SrcComponentDecl.extraMemoryUseInfo()     = " \"" + name() + "\"";
661        eq InstExtends.extraMemoryUseInfo()       = " extending \"" + getClassName().name() + "\"";
662
663    /**
664     * Visit each node in tree (depth-first).
665     */
666    public void ASTNode.visitAll(ASTVisitor v) {
667        for (ASTNode ch : profilingChildren())
668            ch.visitAll(v);
669        v.visit(this);
670    }
671
672        /**
673         * Template for vistor object for tree.
674         */
675        public interface ASTVisitor {
676                public void visit(ASTNode n);
677        }
678       
679        /**
680         * Base class for ASTVisitor filtered by a criteria.
681         */
682        public abstract class FilteredASTVisitor implements ASTVisitor {
683                private Criteria<ASTNode> crit;
684               
685                public FilteredASTVisitor(Criteria<ASTNode> crit) {
686                        this.crit = crit;
687                }
688               
689                public void visit(ASTNode n) {
690                        if (crit.test(n))
691                                filteredVisit(n);
692                }
693               
694                public abstract void filteredVisit(ASTNode n);
695        }
696       
697        /**
698         * A criteria that checks that the object is an instance of a specific class.
699         */
700        public class ClassCriteria<T> implements Criteria<T> {
701                private Class cls;
702               
703                public ClassCriteria(Class cls) {
704                        this.cls = cls;
705                }
706               
707                public boolean test(T elem) {
708                        return cls.isInstance(elem);
709                }
710        }
711
712    /**
713     * Contains methods for calculating the size of AST nodes.
714     *
715     * Uses minimum sizes stipulated by language standard, and ignores padding for memory alignment
716     * used by many JVMs. Thus values should be treated as minimum values.
717     */
718    public abstract class Profiler {
719
720        /**
721         * Approximates the memory footprint of an AST node.
722         *
723         * @param deep  if the approximation should include the contents of non-ASTNode members
724         */
725        public static long getNodeSize(ASTNode node, boolean deep) {
726            if (deep)
727                return getObjectSize(node);
728            else 
729                return getTotalShellSize(node.getClass());
730        }
731
732        /**
733         * Clear cached data.
734         */
735        public static void clear() {
736            visited.clear();
737        }
738
739        private static final long OBJECT_PAD_SIZE     = 8;
740        private static final long OBJECT_SHELL_SIZE   = 12;
741        private static final long OBJREF_SIZE         = 4;
742        private static final long LONG_FIELD_SIZE     = 8;
743        private static final long INT_FIELD_SIZE      = 4;
744        private static final long SHORT_FIELD_SIZE    = 2;
745        private static final long CHAR_FIELD_SIZE     = 2;
746        private static final long BYTE_FIELD_SIZE     = 1;
747        private static final long BOOLEAN_FIELD_SIZE  = 1;
748        private static final long DOUBLE_FIELD_SIZE   = 8;
749        private static final long FLOAT_FIELD_SIZE    = 4;
750
751        private static HashMap<Class, Long> totalFieldSize = new HashMap<Class, Long>();
752        private static IdentityHashMap<Object, Object> visited = new IdentityHashMap<Object, Object>();
753
754        public static class GetFieldsAction implements PrivilegedAction {
755           
756            private Class cls;
757           
758            public Field[] perform(Class cl) {
759                cls = cl;
760                return (Field[]) AccessController.doPrivileged(this);
761            }
762
763            public Object run() {
764                return cls.getDeclaredFields();
765            }
766           
767        }
768
769        public static class GetValueAction implements PrivilegedExceptionAction {
770           
771            private Field field;
772           
773            public Object perform(Field f, Object o) throws Exception {
774                if (!f.isAccessible()) {
775                    field = f;
776                    AccessController.doPrivileged(this);
777                }
778                return f.get(o);
779            }
780
781            public Object run() throws Exception {
782                field.setAccessible(true);
783                return null;
784            }
785           
786        }
787
788        public static final GetFieldsAction GET_FIELDS = new GetFieldsAction();
789        public static final GetValueAction  GET_VALUE  = new GetValueAction();
790
791        public static long getObjectSize(Object o) {
792            if (o == null || visited.containsKey(o))
793                return 0;
794            visited.put(o, null);
795           
796            Class type = o.getClass();
797            if (type.isArray())
798                return getArraySize(o, type);
799            if (o instanceof LinkedList)
800                return getLinkedListSize((LinkedList) o, type);
801           
802            long mem = getTotalShellSize(type);
803            for (; type != null; type = type.getSuperclass())
804                for (Field f : GET_FIELDS.perform(type)) 
805                    if ((f.getModifiers() & Modifier.STATIC) == 0) 
806                        mem += getObjectFieldSize(f, o);
807            return mem;
808        }
809
810        public static void printObjectParts(Object o) {
811            printObjectParts(o, o.getClass());
812        }
813
814        private static void printObjectParts(Object o, Class type) {
815            if (type != Object.class)
816                printObjectParts(o, type.getSuperclass());
817           
818            for (Field f : GET_FIELDS.perform(type)) {
819                if ((f.getModifiers() & Modifier.STATIC) == 0) {
820                    long mem = getFieldSize(f.getType()) + getObjectFieldSize(f, o);
821                    System.out.format("%6d : %s\n", mem, f.getName());
822                }
823            }
824        }
825
826        private static long getObjectFieldSize(Field f, Object o) {
827            Class type = f.getType();
828            if (type.isPrimitive())
829                return 0;
830            try {
831                Object val = GET_VALUE.perform(f, o);
832                return shouldCount(val) ? getObjectSize(val) : 0;
833            } catch (Exception e) {
834                System.err.println("Could not read member: " + o.getClass().getSimpleName() + "." + f.getName());
835                return OBJECT_SHELL_SIZE;
836            }
837        }
838
839        private static boolean shouldCount(Object o) {
840            if (o instanceof ASTNode) {
841                ASTNode n = (ASTNode) o;
842                for (; n.parent != null; n = n.parent)
843                    if (!n.recognizedByParent())
844                        return true;
845                return !(n instanceof Root);
846            } else {
847                return true;
848            }
849        }
850
851        // Special case for linked list to avoid stack overflow
852        private static long getLinkedListSize(LinkedList l, Class type) {
853            long sum = getTotalShellSize(type);
854            long entrySize;
855            try {
856                entrySize = getTotalShellSize(type.getField("header").getType());
857            } catch (Exception e) {
858                entrySize = 3 * OBJREF_SIZE + OBJECT_SHELL_SIZE; // From inspecting source
859            }
860            for (Object o : l)
861                sum += entrySize + getObjectSize(o);
862            return sum;
863        }
864
865        private static long getTotalFieldSize(Class type) {
866            if (totalFieldSize.containsKey(type))
867                return totalFieldSize.get(type);
868           
869            long mem = 0;
870            if (type != Object.class)
871                mem = getTotalFieldSize(type.getSuperclass());
872           
873            for (Field f : GET_FIELDS.perform(type)) 
874                if ((f.getModifiers() & Modifier.STATIC) == 0) 
875                    mem += getFieldSize(f.getType());
876           
877            totalFieldSize.put(type, mem);
878            return mem;
879        }
880
881        public static long getTotalShellSize(Class type) {
882            long mem = OBJECT_SHELL_SIZE + getTotalFieldSize(type);
883            return ((mem - 1) / OBJECT_PAD_SIZE + 1) * OBJECT_PAD_SIZE;
884        }
885
886        private static long getFieldSize(Class type) {
887            if (type == int.class)
888                return INT_FIELD_SIZE;
889            else if (type == long.class)
890                return LONG_FIELD_SIZE;
891            else if (type == short.class)
892                return SHORT_FIELD_SIZE;
893            else if (type == byte.class)
894                return BYTE_FIELD_SIZE;
895            else if (type == boolean.class)
896                return BOOLEAN_FIELD_SIZE;
897            else if (type == char.class)
898                return CHAR_FIELD_SIZE;
899            else if (type == double.class)
900                return DOUBLE_FIELD_SIZE;
901            else if (type == float.class)
902                return FLOAT_FIELD_SIZE;
903            return OBJREF_SIZE;
904        }
905
906        private static long getArraySize(Object o, Class type) {
907            int len = java.lang.reflect.Array.getLength(o);
908            Class comp = type.getComponentType();
909            long size = getFieldSize(comp);
910            long res = OBJECT_SHELL_SIZE + INT_FIELD_SIZE + len * size;
911            res = ((res - 1) / OBJECT_PAD_SIZE + 1) * OBJECT_PAD_SIZE;
912            if (!comp.isPrimitive()) { 
913                for (int i = 0; i < len; i++) {
914                    Object elem = java.lang.reflect.Array.get(o, i);
915                    if (shouldCount(elem))
916                        res += getObjectSize(elem);
917                }
918            }
919            return res;
920        }
921
922    }
923
924        public boolean ASTNode.recognizedByParent() {
925                return parent != null && 
926                                parent.children != null && 
927                                childIndex >= 0 && childIndex < parent.children.length &&
928                                parent.children[childIndex] == this;
929        }
930
931}
Note: See TracBrowser for help on using the repository browser.