diff options
Diffstat (limited to 'tracewriter/src/main/java/io/perfmark/tracewriter/TraceEvent.java')
-rw-r--r-- | tracewriter/src/main/java/io/perfmark/tracewriter/TraceEvent.java | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/tracewriter/src/main/java/io/perfmark/tracewriter/TraceEvent.java b/tracewriter/src/main/java/io/perfmark/tracewriter/TraceEvent.java new file mode 100644 index 0000000..08e8ca5 --- /dev/null +++ b/tracewriter/src/main/java/io/perfmark/tracewriter/TraceEvent.java @@ -0,0 +1,267 @@ +/* + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.perfmark.tracewriter; + +import com.google.gson.annotations.SerializedName; +import io.perfmark.impl.Mark; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; +import javax.annotation.CheckReturnValue; +import javax.annotation.Nullable; + +@CheckReturnValue +final class TraceEvent implements Cloneable { + + private TraceEvent() {} + + static final TraceEvent EVENT = new TraceEvent(); + + @SerializedName("ph") + @SuppressWarnings("unused") + private String phase; + + @SerializedName("name") + @SuppressWarnings("unused") + private String name; + + @Nullable + @SerializedName("cat") + @SuppressWarnings("unused") + private String categories; + + @Nullable + @SerializedName("ts") + @SuppressWarnings("unused") + private Double traceClockMicros; + + @Nullable + @SerializedName("pid") + @SuppressWarnings("unused") + private Long pid; + + @SerializedName("tid") + @Nullable + @SuppressWarnings("unused") + private Long tid; + + @Nullable + @SerializedName("id") + @SuppressWarnings("unused") + private Long id; + + @Nullable + @SerializedName("args") + @SuppressWarnings("unused") + private TagMap args = null; + + @Nullable + @SerializedName("cname") + @SuppressWarnings("unused") + private String colorName = null; + + TraceEvent name(String name) { + if (name == null) { + throw new NullPointerException("name"); + } + TraceEvent other = clone(); + other.name = name; + return other; + } + + TraceEvent categories(String... categories) { + if (categories == null) { + throw new NullPointerException("categories"); + } + return categories(Arrays.asList(categories)); + } + + TraceEvent categories(List<String> categories) { + if (categories == null) { + throw new NullPointerException("categories"); + } + TraceEvent other = clone(); + if (!categories.isEmpty()) { + StringBuilder sb = new StringBuilder(); + ListIterator<String> it = categories.listIterator(); + sb.append(it.next()); + while (it.hasNext()) { + String next = it.next(); + if (next == null) { + throw new NullPointerException("next null at " + (it.nextIndex() - 1)); + } + sb.append(',').append(next); + } + other.categories = sb.toString(); + } else { + other.categories = null; + } + return other; + } + + strictfp TraceEvent traceClockNanos(long traceClockNanos) { + TraceEvent other = clone(); + other.traceClockMicros = traceClockNanos / 1000.0; + return other; + } + + TraceEvent phase(String phase) { + if (phase == null) { + throw new NullPointerException("phase"); + } + TraceEvent other = clone(); + other.phase = phase; + return other; + } + + TraceEvent tid(long tid) { + TraceEvent other = clone(); + other.tid = tid; + return other; + } + + TraceEvent pid(long pid) { + TraceEvent other = clone(); + other.pid = pid; + return other; + } + + TraceEvent id(long id) { + TraceEvent other = clone(); + other.id = id; + return other; + } + + /** + * Note This should only be used for tags, as the map size is used to determine the arg names in + * TraceEventWriter. This will overwrite any existing args. + * + * @param tagMap the args to use. + * @return this + */ + TraceEvent args(TagMap tagMap) { + if (tagMap == null) { + throw new NullPointerException("tagMap"); + } + TraceEvent other = clone(); + other.args = tagMap; + return other; + } + + TagMap args() { + if (args == null) { + return TagMap.EMPTY; + } else { + return args; + } + } + + @Override + protected TraceEvent clone() { + try { + return (TraceEvent) super.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } + + static final class TagMap extends AbstractMap<String, Object> { + + static final TagMap EMPTY = + new TagMap(Collections.<Entry<String, ?>>emptyList(), Collections.emptyList()); + + private final List<Entry<String, ?>> keyedValues; + private final List<?> unkeyedValues; + + private TagMap(List<Entry<String, ?>> keyedValues, List<?> unkeyedValues) { + this.keyedValues = keyedValues; + this.unkeyedValues = unkeyedValues; + } + + TagMap withUnkeyed(@Nullable String tagName, long tagId) { + List<Object> unkeyedValues = null; + if (tagName != null && !Mark.NO_TAG_NAME.equals(tagName)) { + unkeyedValues = new ArrayList<>(this.unkeyedValues); + unkeyedValues.add(tagName); + } + if (tagId != Mark.NO_TAG_ID) { + unkeyedValues = unkeyedValues != null ? unkeyedValues : new ArrayList<>(this.unkeyedValues); + unkeyedValues.add(tagId); + } + if (unkeyedValues != null) { + return new TagMap(keyedValues, Collections.unmodifiableList(unkeyedValues)); + } else { + return new TagMap(keyedValues, this.unkeyedValues); + } + } + + TagMap withKeyed(@Nullable String tagName, Object tagValue) { + List<Entry<String, ?>> keyedValues = new ArrayList<>(this.keyedValues); + keyedValues.add(new SimpleImmutableEntry<>(String.valueOf(tagName), tagValue)); + return new TagMap(Collections.unmodifiableList(keyedValues), unkeyedValues); + } + + TagMap withKeyed(@Nullable String tagName, long tagValue0, long tagValue1) { + List<Entry<String, ?>> keyedValues = new ArrayList<>(this.keyedValues); + keyedValues.add( + new SimpleImmutableEntry<>(String.valueOf(tagName), tagValue0 + ":" + tagValue1)); + return new TagMap(Collections.unmodifiableList(keyedValues), unkeyedValues); + } + + @Override + public Set<Entry<String, Object>> entrySet() { + List<Entry<String, ?>> pairs = new ArrayList<>(keyedValues.size() + unkeyedValues.size()); + pairs.addAll(keyedValues); + for (Object value : unkeyedValues) { + if (value instanceof Long) { + pairs.add(new SimpleImmutableEntry<>("id", value)); + } else if (value instanceof String) { + pairs.add(new SimpleImmutableEntry<>("tag", value)); + } else { + pairs.add(new SimpleImmutableEntry<>("tag", String.valueOf(value))); + } + } + + Map<String, Object> ret = new LinkedHashMap<>(); + addEntry: + for (Entry<String, ?> kv : pairs) { + String name = kv.getKey(); + Object value = kv.getValue(); + String derivedName = name; + int usages = 0; + while (true) { + if (!ret.containsKey(derivedName)) { + ret.put(derivedName, value); + continue addEntry; + } + if (ret.get(derivedName).equals(value)) { + continue addEntry; + } + usages++; + derivedName = name + " (" + usages + ')'; + } + } + return Collections.unmodifiableSet(ret.entrySet()); + } + } +} |