• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 Google LLC
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package io.perfmark.tracewriter;
18 
19 import com.google.gson.annotations.SerializedName;
20 import io.perfmark.impl.Mark;
21 import java.util.AbstractMap;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collections;
25 import java.util.LinkedHashMap;
26 import java.util.List;
27 import java.util.ListIterator;
28 import java.util.Map;
29 import java.util.Set;
30 import javax.annotation.CheckReturnValue;
31 import javax.annotation.Nullable;
32 
33 @CheckReturnValue
34 final class TraceEvent implements Cloneable {
35 
TraceEvent()36   private TraceEvent() {}
37 
38   static final TraceEvent EVENT = new TraceEvent();
39 
40   @SerializedName("ph")
41   @SuppressWarnings("unused")
42   private String phase;
43 
44   @SerializedName("name")
45   @SuppressWarnings("unused")
46   private String name;
47 
48   @Nullable
49   @SerializedName("cat")
50   @SuppressWarnings("unused")
51   private String categories;
52 
53   @Nullable
54   @SerializedName("ts")
55   @SuppressWarnings("unused")
56   private Double traceClockMicros;
57 
58   @Nullable
59   @SerializedName("pid")
60   @SuppressWarnings("unused")
61   private Long pid;
62 
63   @SerializedName("tid")
64   @Nullable
65   @SuppressWarnings("unused")
66   private Long tid;
67 
68   @Nullable
69   @SerializedName("id")
70   @SuppressWarnings("unused")
71   private Long id;
72 
73   @Nullable
74   @SerializedName("args")
75   @SuppressWarnings("unused")
76   private TagMap args = null;
77 
78   @Nullable
79   @SerializedName("cname")
80   @SuppressWarnings("unused")
81   private String colorName = null;
82 
name(String name)83   TraceEvent name(String name) {
84     if (name == null) {
85       throw new NullPointerException("name");
86     }
87     TraceEvent other = clone();
88     other.name = name;
89     return other;
90   }
91 
categories(String... categories)92   TraceEvent categories(String... categories) {
93     if (categories == null) {
94       throw new NullPointerException("categories");
95     }
96     return categories(Arrays.asList(categories));
97   }
98 
categories(List<String> categories)99   TraceEvent categories(List<String> categories) {
100     if (categories == null) {
101       throw new NullPointerException("categories");
102     }
103     TraceEvent other = clone();
104     if (!categories.isEmpty()) {
105       StringBuilder sb = new StringBuilder();
106       ListIterator<String> it = categories.listIterator();
107       sb.append(it.next());
108       while (it.hasNext()) {
109         String next = it.next();
110         if (next == null) {
111           throw new NullPointerException("next null at " + (it.nextIndex() - 1));
112         }
113         sb.append(',').append(next);
114       }
115       other.categories = sb.toString();
116     } else {
117       other.categories = null;
118     }
119     return other;
120   }
121 
traceClockNanos(long traceClockNanos)122   strictfp TraceEvent traceClockNanos(long traceClockNanos) {
123     TraceEvent other = clone();
124     other.traceClockMicros = traceClockNanos / 1000.0;
125     return other;
126   }
127 
phase(String phase)128   TraceEvent phase(String phase) {
129     if (phase == null) {
130       throw new NullPointerException("phase");
131     }
132     TraceEvent other = clone();
133     other.phase = phase;
134     return other;
135   }
136 
tid(long tid)137   TraceEvent tid(long tid) {
138     TraceEvent other = clone();
139     other.tid = tid;
140     return other;
141   }
142 
pid(long pid)143   TraceEvent pid(long pid) {
144     TraceEvent other = clone();
145     other.pid = pid;
146     return other;
147   }
148 
id(long id)149   TraceEvent id(long id) {
150     TraceEvent other = clone();
151     other.id = id;
152     return other;
153   }
154 
155   /**
156    * Note This should only be used for tags, as the map size is used to determine the arg names in
157    * TraceEventWriter. This will overwrite any existing args.
158    *
159    * @param tagMap the args to use.
160    * @return this
161    */
args(TagMap tagMap)162   TraceEvent args(TagMap tagMap) {
163     if (tagMap == null) {
164       throw new NullPointerException("tagMap");
165     }
166     TraceEvent other = clone();
167     other.args = tagMap;
168     return other;
169   }
170 
args()171   TagMap args() {
172     if (args == null) {
173       return TagMap.EMPTY;
174     } else {
175       return args;
176     }
177   }
178 
179   @Override
clone()180   protected TraceEvent clone() {
181     try {
182       return (TraceEvent) super.clone();
183     } catch (CloneNotSupportedException e) {
184       throw new RuntimeException(e);
185     }
186   }
187 
188   static final class TagMap extends AbstractMap<String, Object> {
189 
190     static final TagMap EMPTY =
191         new TagMap(Collections.<Entry<String, ?>>emptyList(), Collections.emptyList());
192 
193     private final List<Entry<String, ?>> keyedValues;
194     private final List<?> unkeyedValues;
195 
TagMap(List<Entry<String, ?>> keyedValues, List<?> unkeyedValues)196     private TagMap(List<Entry<String, ?>> keyedValues, List<?> unkeyedValues) {
197       this.keyedValues = keyedValues;
198       this.unkeyedValues = unkeyedValues;
199     }
200 
withUnkeyed(@ullable String tagName, long tagId)201     TagMap withUnkeyed(@Nullable String tagName, long tagId) {
202       List<Object> unkeyedValues = null;
203       if (tagName != null && !Mark.NO_TAG_NAME.equals(tagName)) {
204         unkeyedValues = new ArrayList<>(this.unkeyedValues);
205         unkeyedValues.add(tagName);
206       }
207       if (tagId != Mark.NO_TAG_ID) {
208         unkeyedValues = unkeyedValues != null ? unkeyedValues : new ArrayList<>(this.unkeyedValues);
209         unkeyedValues.add(tagId);
210       }
211       if (unkeyedValues != null) {
212         return new TagMap(keyedValues, Collections.unmodifiableList(unkeyedValues));
213       } else {
214         return new TagMap(keyedValues, this.unkeyedValues);
215       }
216     }
217 
withKeyed(@ullable String tagName, Object tagValue)218     TagMap withKeyed(@Nullable String tagName, Object tagValue) {
219       List<Entry<String, ?>> keyedValues = new ArrayList<>(this.keyedValues);
220       keyedValues.add(new SimpleImmutableEntry<>(String.valueOf(tagName), tagValue));
221       return new TagMap(Collections.unmodifiableList(keyedValues), unkeyedValues);
222     }
223 
withKeyed(@ullable String tagName, long tagValue0, long tagValue1)224     TagMap withKeyed(@Nullable String tagName, long tagValue0, long tagValue1) {
225       List<Entry<String, ?>> keyedValues = new ArrayList<>(this.keyedValues);
226       keyedValues.add(
227           new SimpleImmutableEntry<>(String.valueOf(tagName), tagValue0 + ":" + tagValue1));
228       return new TagMap(Collections.unmodifiableList(keyedValues), unkeyedValues);
229     }
230 
231     @Override
entrySet()232     public Set<Entry<String, Object>> entrySet() {
233       List<Entry<String, ?>> pairs = new ArrayList<>(keyedValues.size() + unkeyedValues.size());
234       pairs.addAll(keyedValues);
235       for (Object value : unkeyedValues) {
236         if (value instanceof Long) {
237           pairs.add(new SimpleImmutableEntry<>("id", value));
238         } else if (value instanceof String) {
239           pairs.add(new SimpleImmutableEntry<>("tag", value));
240         } else {
241           pairs.add(new SimpleImmutableEntry<>("tag", String.valueOf(value)));
242         }
243       }
244 
245       Map<String, Object> ret = new LinkedHashMap<>();
246       addEntry:
247       for (Entry<String, ?> kv : pairs) {
248         String name = kv.getKey();
249         Object value = kv.getValue();
250         String derivedName = name;
251         int usages = 0;
252         while (true) {
253           if (!ret.containsKey(derivedName)) {
254             ret.put(derivedName, value);
255             continue addEntry;
256           }
257           if (ret.get(derivedName).equals(value)) {
258             continue addEntry;
259           }
260           usages++;
261           derivedName = name + " (" + usages + ')';
262         }
263       }
264       return Collections.unmodifiableSet(ret.entrySet());
265     }
266   }
267 }
268