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.replacements;
24
25//JaCoCo Exclude
26
27import java.io.PrintStream;
28import java.util.ArrayList;
29import java.util.Collections;
30import java.util.List;
31
32/**
33 * A counter that can be safely {@linkplain #inc() incremented} from within a snippet for gathering
34 * snippet specific metrics.
35 */
36public class SnippetCounter implements Comparable<SnippetCounter> {
37    /**
38     * A group of related counters.
39     */
40    public static class Group {
41
42        final String name;
43        final List<SnippetCounter> counters;
44
45        public Group(String name) {
46            this.name = name;
47            this.counters = new ArrayList<>();
48        }
49
50        @Override
51        public synchronized String toString() {
52            Collections.sort(counters);
53
54            long total = 0;
55            int maxNameLen = 0;
56            for (SnippetCounter c : counters) {
57                total += c.value;
58                maxNameLen = Math.max(c.name.length(), maxNameLen);
59            }
60
61            StringBuilder buf = new StringBuilder(String.format("Counters: %s%n", name));
62
63            String formatString = "  %" + maxNameLen + "s: %6.2f%%%," + (String.format("%,d", total).length() + 2) + "d  // %s%n";
64            for (SnippetCounter c : counters) {
65                double percent = total == 0D ? 0D : ((double) (c.value * 100)) / total;
66                buf.append(String.format(formatString, c.name, percent, c.value, c.description));
67            }
68            buf.append(String.format(formatString, "TOTAL", 100.0D, total, ""));
69
70            return buf.toString();
71        }
72    }
73
74    /**
75     * Sorts counters in descending order of their {@linkplain #value() values}.
76     */
77    @Override
78    public int compareTo(SnippetCounter o) {
79        if (value > o.value) {
80            return -1;
81        } else if (o.value < value) {
82            return 1;
83        }
84        return 0;
85    }
86
87    private static final List<Group> groups = new ArrayList<>();
88
89    private final Group group;
90    private final int index;
91    private final String name;
92    private final String description;
93    private long value;
94
95    /**
96     * Creates a counter.
97     *
98     * @param group the group to which the counter belongs. If this is null, the newly created
99     *            counter is disabled and {@linkplain #inc() incrementing} is a no-op.
100     * @param name the name of the counter
101     * @param description a brief comment describing the metric represented by the counter
102     */
103    public SnippetCounter(Group group, String name, String description) {
104        this.group = group;
105        this.name = name;
106        this.description = description;
107        if (group != null) {
108            List<SnippetCounter> counters = group.counters;
109            this.index = counters.size();
110            counters.add(this);
111            if (index == 0) {
112                groups.add(group);
113            }
114        } else {
115            this.index = -1;
116        }
117    }
118
119    /**
120     * Increments the value of this counter. This method can only be used in a snippet on a
121     * compile-time constant {@link SnippetCounter} object.
122     */
123    public void inc() {
124        if (group != null) {
125            SnippetCounterNode.increment(this);
126        }
127    }
128
129    /**
130     * Increments the value of this counter. This method can only be used in a snippet on a
131     * compile-time constant {@link SnippetCounter} object.
132     */
133    public void add(int increment) {
134        if (group != null) {
135            SnippetCounterNode.add(this, increment);
136        }
137    }
138
139    /**
140     * Gets the value of this counter.
141     */
142    public long value() {
143        return value;
144    }
145
146    @Override
147    public String toString() {
148        if (group != null) {
149            return "SnippetCounter-" + group.name + ":" + name;
150        }
151        return super.toString();
152    }
153
154    /**
155     * Prints all the counter groups to a given stream.
156     */
157    public static void printGroups(PrintStream out) {
158        for (Group group : groups) {
159            out.println(group);
160        }
161    }
162}
163