001/*
002 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.hotspot;
024
025import static com.oracle.graal.debug.GraalDebugConfig.*;
026
027import java.io.*;
028import java.util.*;
029import java.util.regex.*;
030import java.util.stream.*;
031
032import jdk.internal.jvmci.common.*;
033
034import com.oracle.graal.debug.*;
035import com.oracle.graal.debug.internal.*;
036
037/**
038 * Facility for printing the {@linkplain KeyRegistry#getDebugValues() values} collected across all
039 * {@link DebugValueMap#getTopLevelMaps() threads}.
040 */
041public class DebugValuesPrinter {
042
043    public void printDebugValues() throws JVMCIError {
044        TTY.println();
045        TTY.println("<DebugValues>");
046        List<DebugValueMap> topLevelMaps = DebugValueMap.getTopLevelMaps();
047        List<DebugValue> debugValues = KeyRegistry.getDebugValues();
048        if (debugValues.size() > 0) {
049            try {
050                ArrayList<DebugValue> sortedValues = new ArrayList<>(debugValues);
051                Collections.sort(sortedValues);
052
053                String summary = DebugValueSummary.getValue();
054                if (summary == null) {
055                    summary = "Complete";
056                }
057                if (DebugValueThreadFilter.getValue() != null && topLevelMaps.size() != 0) {
058                    topLevelMaps = topLevelMaps.stream().filter(map -> Pattern.compile(DebugValueThreadFilter.getValue()).matcher(map.getName()).find()).collect(Collectors.toList());
059                    if (topLevelMaps.size() == 0) {
060                        TTY.println("Warning: DebugValueThreadFilter=%s eliminated all maps so nothing will be printed", DebugValueThreadFilter.getValue());
061                    }
062                }
063                switch (summary) {
064                    case "Name":
065                        printSummary(topLevelMaps, sortedValues);
066                        break;
067                    case "Partial": {
068                        DebugValueMap globalMap = new DebugValueMap("Global");
069                        for (DebugValueMap map : topLevelMaps) {
070                            flattenChildren(map, globalMap);
071                        }
072                        globalMap.normalize();
073                        printMap(new DebugValueScope(null, globalMap), sortedValues);
074                        break;
075                    }
076                    case "Complete": {
077                        DebugValueMap globalMap = new DebugValueMap("Global");
078                        for (DebugValueMap map : topLevelMaps) {
079                            globalMap.addChild(map);
080                        }
081                        globalMap.group();
082                        globalMap.normalize();
083                        printMap(new DebugValueScope(null, globalMap), sortedValues);
084                        break;
085                    }
086                    case "Thread":
087                        for (DebugValueMap map : topLevelMaps) {
088                            TTY.println("Showing the results for thread: " + map.getName());
089                            map.group();
090                            map.normalize();
091                            printMap(new DebugValueScope(null, map), sortedValues);
092                        }
093                        break;
094                    default:
095                        throw new JVMCIError("Unknown summary type: %s", summary);
096                }
097                for (DebugValueMap topLevelMap : topLevelMaps) {
098                    topLevelMap.reset();
099                }
100            } catch (Throwable e) {
101                // Don't want this to change the exit status of the VM
102                PrintStream err = System.err;
103                err.println("Error while printing debug values:");
104                e.printStackTrace();
105            }
106        }
107        TTY.println("</DebugValues>");
108    }
109
110    private void flattenChildren(DebugValueMap map, DebugValueMap globalMap) {
111        globalMap.addChild(map);
112        for (DebugValueMap child : map.getChildren()) {
113            flattenChildren(child, globalMap);
114        }
115        map.clearChildren();
116    }
117
118    private void printSummary(List<DebugValueMap> topLevelMaps, List<DebugValue> debugValues) {
119        DebugValueMap result = new DebugValueMap("Summary");
120        for (int i = debugValues.size() - 1; i >= 0; i--) {
121            DebugValue debugValue = debugValues.get(i);
122            int index = debugValue.getIndex();
123            long total = collectTotal(topLevelMaps, index);
124            result.setCurrentValue(index, total);
125        }
126        printMap(new DebugValueScope(null, result), debugValues);
127    }
128
129    private long collectTotal(List<DebugValueMap> maps, int index) {
130        long total = 0;
131        for (int i = 0; i < maps.size(); i++) {
132            DebugValueMap map = maps.get(i);
133            total += map.getCurrentValue(index);
134            total += collectTotal(map.getChildren(), index);
135        }
136        return total;
137    }
138
139    /**
140     * Tracks the scope when printing a {@link DebugValueMap}, allowing "empty" scopes to be
141     * omitted. An empty scope is one in which there are no (nested) non-zero debug values.
142     */
143    static class DebugValueScope {
144
145        final DebugValueScope parent;
146        final int level;
147        final DebugValueMap map;
148        private boolean printed;
149
150        public DebugValueScope(DebugValueScope parent, DebugValueMap map) {
151            this.parent = parent;
152            this.map = map;
153            this.level = parent == null ? 0 : parent.level + 1;
154        }
155
156        public void print() {
157            if (!printed) {
158                printed = true;
159                if (parent != null) {
160                    parent.print();
161                }
162                printIndent(level);
163                TTY.println("%s", map.getName());
164            }
165        }
166    }
167
168    private void printMap(DebugValueScope scope, List<DebugValue> debugValues) {
169
170        for (DebugValue value : debugValues) {
171            long l = scope.map.getCurrentValue(value.getIndex());
172            if (l != 0 || !SuppressZeroDebugValues.getValue()) {
173                scope.print();
174                printIndent(scope.level + 1);
175                TTY.println(value.getName() + "=" + value.toString(l));
176            }
177        }
178
179        for (DebugValueMap child : scope.map.getChildren()) {
180            printMap(new DebugValueScope(scope, child), debugValues);
181        }
182    }
183
184    private static void printIndent(int level) {
185        for (int i = 0; i < level; ++i) {
186            TTY.print("    ");
187        }
188        TTY.print("|-> ");
189    }
190}