aboutsummaryrefslogtreecommitdiff
path: root/tracewriter/src/main/java/io/perfmark/tracewriter/TraceEvent.java
diff options
context:
space:
mode:
Diffstat (limited to 'tracewriter/src/main/java/io/perfmark/tracewriter/TraceEvent.java')
-rw-r--r--tracewriter/src/main/java/io/perfmark/tracewriter/TraceEvent.java267
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());
+ }
+ }
+}