1/*
2 * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23package org.graalvm.compiler.debug;
24
25import java.util.ArrayList;
26import java.util.Arrays;
27import java.util.Collections;
28import java.util.HashMap;
29import java.util.List;
30import java.util.Map;
31
32/**
33 * A node in a tree of values.
34 */
35public class DebugValueMap {
36
37    /**
38     * The top level maps for all threads.
39     */
40    private static final List<DebugValueMap> topLevelMaps = new ArrayList<>();
41
42    private long[] values;
43    private List<DebugValueMap> children;
44    private String name;
45
46    public DebugValueMap(String name) {
47        this.name = name;
48    }
49
50    public void setCurrentValue(int index, long l) {
51        ensureSize(index);
52        values[index] = l;
53    }
54
55    public long getCurrentValue(int index) {
56        ensureSize(index);
57        return values[index];
58    }
59
60    public void clearChildren() {
61        if (children != null) {
62            children.clear();
63        }
64    }
65
66    public void reset() {
67        if (values != null) {
68            Arrays.fill(values, 0L);
69        }
70        if (children != null) {
71            for (DebugValueMap child : children) {
72                child.reset();
73            }
74        }
75    }
76
77    private void ensureSize(int index) {
78        if (values == null) {
79            values = new long[index + 1];
80        }
81        if (values.length <= index) {
82            values = Arrays.copyOf(values, index + 1);
83        }
84    }
85
86    private int capacity() {
87        return (values == null) ? 0 : values.length;
88    }
89
90    public void addChild(DebugValueMap map) {
91        if (children == null) {
92            children = new ArrayList<>(4);
93        }
94        children.add(map);
95    }
96
97    public List<DebugValueMap> getChildren() {
98        if (children == null) {
99            return Collections.emptyList();
100        } else {
101            return Collections.unmodifiableList(children);
102        }
103    }
104
105    public boolean hasChildren() {
106        return children != null && !children.isEmpty();
107    }
108
109    public String getName() {
110        return this.name;
111    }
112
113    @Override
114    public String toString() {
115        return "DebugValueMap<" + getName() + ">";
116    }
117
118    public static synchronized void registerTopLevel(DebugValueMap map) {
119        topLevelMaps.add(map);
120    }
121
122    public static synchronized List<DebugValueMap> getTopLevelMaps() {
123        return topLevelMaps;
124    }
125
126    /**
127     * The top level map for the current thread.
128     */
129    private static final ThreadLocal<DebugValueMap> topLevelMap = new ThreadLocal<>();
130
131    static DebugValueMap getTopLevelMap() {
132        DebugValueMap map = topLevelMap.get();
133        if (map == null) {
134            map = new DebugValueMap(Thread.currentThread().getName());
135            topLevelMap.set(map);
136            registerTopLevel(map);
137        }
138        return map;
139    }
140
141    public void normalize() {
142        if (hasChildren()) {
143            Map<String, DebugValueMap> occurred = new HashMap<>();
144            for (DebugValueMap map : children) {
145                String mapName = map.getName();
146                if (!occurred.containsKey(mapName)) {
147                    occurred.put(mapName, map);
148                    map.normalize();
149                } else {
150                    occurred.get(mapName).mergeWith(map);
151                    occurred.get(mapName).normalize();
152                }
153            }
154
155            if (occurred.values().size() < children.size()) {
156                // At least one duplicate was found.
157                children.clear();
158                for (DebugValueMap map : occurred.values()) {
159                    addChild(map);
160                    map.normalize();
161                }
162            }
163        }
164    }
165
166    private void mergeWith(DebugValueMap map) {
167        if (map.hasChildren()) {
168            if (hasChildren()) {
169                children.addAll(map.children);
170            } else {
171                children = map.children;
172            }
173            map.children = null;
174        }
175
176        int size = Math.max(this.capacity(), map.capacity());
177        ensureSize(size);
178        for (int i = 0; i < size; ++i) {
179            long curValue = getCurrentValue(i);
180            long otherValue = map.getCurrentValue(i);
181            setCurrentValue(i, curValue + otherValue);
182        }
183    }
184
185    public void group() {
186        if (this.hasChildren()) {
187            List<DebugValueMap> oldChildren = new ArrayList<>(this.children);
188            this.children.clear();
189            for (DebugValueMap map : oldChildren) {
190                mergeWith(map);
191            }
192        }
193    }
194}
195