From 8285c4e2aa1c5a51bd15e45f31ce34516bee4987 Mon Sep 17 00:00:00 2001 From: Carl Mastrangelo Date: Wed, 20 Dec 2023 22:17:49 -0800 Subject: [PATCH] api: Add Int,Long, and String Tag variants --- api/src/main/java/io/perfmark/Impl.java | 13 ++- api/src/main/java/io/perfmark/PerfMark.java | 61 +++++++++++++- .../main/java/io/perfmark/StringFunction.java | 6 +- .../java/io/perfmark/CompatibilityTest.java | 20 +++++ .../test/java/io/perfmark/PerfMarkTest.java | 29 ++++--- .../io/perfmark/impl/SecretPerfMarkImpl.java | 79 +++++++++++++++++-- 6 files changed, 185 insertions(+), 23 deletions(-) diff --git a/api/src/main/java/io/perfmark/Impl.java b/api/src/main/java/io/perfmark/Impl.java index 7e33b6fb..10a37d88 100644 --- a/api/src/main/java/io/perfmark/Impl.java +++ b/api/src/main/java/io/perfmark/Impl.java @@ -16,6 +16,10 @@ package io.perfmark; +import java.util.function.Function; +import java.util.function.ToIntFunction; +import java.util.function.ToLongFunction; + public class Impl { static final String NO_TAG_NAME = ""; static final long NO_TAG_ID = Long.MIN_VALUE; @@ -42,7 +46,7 @@ protected boolean setEnabled(boolean value, boolean overload) { return false; } - protected void startTask(T taskNameObject, StringFunction taskNameFunc) {} + protected void startTask(T taskNameObject, Function taskNameFunc) {} protected void startTask(String taskName, Tag tag) {} @@ -79,7 +83,12 @@ protected void attachTag(String tagName, long tagValue) {} protected void attachTag(String tagName, long tagValue0, long tagValue1) {} protected void attachTag( - String tagName, T tagObject, StringFunction stringFunction) {} + String tagName, T tagObject, Function stringFunction) {} + + protected void attachTag(String tagName, T tagObject, ToIntFunction intFunction) {} + + protected void attachTag( + String tagName, T tagObject, ToLongFunction longFunction) {} protected Tag createTag(String tagName, long tagId) { return NO_TAG; diff --git a/api/src/main/java/io/perfmark/PerfMark.java b/api/src/main/java/io/perfmark/PerfMark.java index a064e988..6382feb1 100644 --- a/api/src/main/java/io/perfmark/PerfMark.java +++ b/api/src/main/java/io/perfmark/PerfMark.java @@ -20,6 +20,9 @@ import com.google.errorprone.annotations.DoNotCall; import com.google.errorprone.annotations.MustBeClosed; import java.lang.reflect.Method; +import java.util.function.Function; +import java.util.function.ToIntFunction; +import java.util.function.ToLongFunction; /** * PerfMark is a very low overhead tracing library. To use PerfMark, annotate the code that needs to @@ -546,7 +549,27 @@ public static void attachTag(String tagName, long tagValue0, long tagValue1) { * * @param tagName The name of the value being attached * @param tagObject The tag object which will passed to the stringFunction. - * @param stringFunction The function that will convert the object to + * @param stringFunction The function that will convert the object to a tag + * @param the type of tag object to be stringified + * @since 0.27.0 + */ + public static void attachStringTag( + String tagName, T tagObject, Function stringFunction) { + impl.attachTag(tagName, tagObject, stringFunction); + } + + /** + * Attaches an additional keyed tag to the current active task. The tag provided is independent of + * the tag used with {@code startTask} and {@code stopTask}. This tag operation is different than + * {@link Tag} in that the tag value has an associated name (also called a key). The tag name and + * value are attached to the most recently started task, and don't have to match any other tags. + * This method is useful for when you have the tag information after the task is started. + * + *

Prefer {@link #attachStringTag(String, Object, Function)} over this one. + * + * @param tagName The name of the value being attached + * @param tagObject The tag object which will passed to the stringFunction. + * @param stringFunction The function that will convert the object to a tag * @param the type of tag object to be stringified * @since 0.22.0 */ @@ -555,6 +578,42 @@ public static void attachTag( impl.attachTag(tagName, tagObject, stringFunction); } + /** + * Attaches an additional keyed tag to the current active task. The tag provided is independent of + * the tag used with {@code startTask} and {@code stopTask}. This tag operation is different than + * {@link Tag} in that the tag value has an associated name (also called a key). The tag name and + * value are attached to the most recently started task, and don't have to match any other tags. + * This method is useful for when you have the tag information after the task is started. + * + * @param tagName The name of the value being attached + * @param tagObject The tag object which will passed to the intFunction. + * @param intFunction The function that will convert the object to a tag + * @param the type of tag object to mapped to in. + * @since 0.27.0 + */ + public static void attachIntTag( + String tagName, T tagObject, ToIntFunction intFunction) { + impl.attachTag(tagName, tagObject, intFunction); + } + + /** + * Attaches an additional keyed tag to the current active task. The tag provided is independent of + * the tag used with {@code startTask} and {@code stopTask}. This tag operation is different than + * {@link Tag} in that the tag value has an associated name (also called a key). The tag name and + * value are attached to the most recently started task, and don't have to match any other tags. + * This method is useful for when you have the tag information after the task is started. + * + * @param tagName The name of the value being attached + * @param tagObject The tag object which will passed to the intFunction. + * @param longFunction The function that will convert the object to a tag + * @param the type of tag object to mapped to in. + * @since 0.27.0 + */ + public static void attachLongTag( + String tagName, T tagObject, ToLongFunction longFunction) { + impl.attachTag(tagName, tagObject, longFunction); + } + private static final Impl impl; static { diff --git a/api/src/main/java/io/perfmark/StringFunction.java b/api/src/main/java/io/perfmark/StringFunction.java index 19506e02..8360af05 100644 --- a/api/src/main/java/io/perfmark/StringFunction.java +++ b/api/src/main/java/io/perfmark/StringFunction.java @@ -16,6 +16,8 @@ package io.perfmark; +import java.util.function.Function; + /** * This interface is equivalent to {@code java.util.function.Function}. It is here as a * compatibility shim to make PerfMark compatible with Java 6. This will likely be removed if @@ -24,7 +26,8 @@ * @since 0.22.0 * @param The type to turn into a String. */ -public interface StringFunction { +@FunctionalInterface +public interface StringFunction extends Function { /** * Takes the given argument and produces a String. @@ -33,5 +36,6 @@ public interface StringFunction { * @param t the subject to Stringify * @return the String */ + @Override String apply(T t); } diff --git a/api/src/test/java/io/perfmark/CompatibilityTest.java b/api/src/test/java/io/perfmark/CompatibilityTest.java index 3a6013db..3381bd93 100644 --- a/api/src/test/java/io/perfmark/CompatibilityTest.java +++ b/api/src/test/java/io/perfmark/CompatibilityTest.java @@ -420,6 +420,26 @@ public void attachTag_namedFunction() throws Exception { assertThat(marks).hasSize(3); } + @Test + public void implOverride() throws Exception { + Assume.assumeTrue(minorVersion >= 17); + + Class implClass = Class.forName("io.perfmark.Impl", false, perfMarkClz.getClassLoader()); + Class secretImplClass = + Class.forName( + "io.perfmark.impl.SecretPerfMarkImpl$PerfMarkImpl", + false, + perfMarkClz.getClassLoader()); + + for (Method method : implClass.getDeclaredMethods()) { + if ((method.getModifiers() & Modifier.STATIC) != 0) { + continue; + } + Method m2 = secretImplClass.getDeclaredMethod(method.getName(), method.getParameterTypes()); + assertThat(m2).isNotNull(); + } + } + private final class ApiOverrideClassLoader extends ClassLoader { @Override protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { diff --git a/api/src/test/java/io/perfmark/PerfMarkTest.java b/api/src/test/java/io/perfmark/PerfMarkTest.java index 645edf37..0eac6721 100644 --- a/api/src/test/java/io/perfmark/PerfMarkTest.java +++ b/api/src/test/java/io/perfmark/PerfMarkTest.java @@ -114,6 +114,9 @@ public void allMethodForward_taskName() { PerfMark.startTask("task5", String::valueOf); PerfMark.attachTag(PerfMark.createTag("extra")); PerfMark.attachTag("name", "extra2", String::valueOf); + PerfMark.attachStringTag("name", "extra3", String::valueOf); + PerfMark.attachIntTag("name", List.of(), List::size); + PerfMark.attachLongTag("name", 2d, Double::longValue); Link link = PerfMark.linkOut(); PerfMark.linkIn(link); PerfMark.stopTask(); @@ -127,7 +130,7 @@ public void allMethodForward_taskName() { List marks = new ArrayList<>(Storage.readForTest()); - Truth.assertThat(marks).hasSize(24); + Truth.assertThat(marks).hasSize(27); List expected = Arrays.asList( Mark.taskStart(gen, marks.get(0).getNanoTime(), "task1"), @@ -140,20 +143,23 @@ public void allMethodForward_taskName() { Mark.taskStart(gen, marks.get(7).getNanoTime(), "task5"), Mark.tag(gen, "extra", NO_TAG_ID), Mark.keyedTag(gen, "name", "extra2"), + Mark.keyedTag(gen, "name", "extra3"), + Mark.keyedTag(gen, "name", 0), + Mark.keyedTag(gen, "name", 2), Mark.link(gen, link.linkId), Mark.link(gen, -link.linkId), - Mark.taskEnd(gen, marks.get(12).getNanoTime()), - Mark.taskEnd(gen, marks.get(13).getNanoTime(), "task4"), + Mark.taskEnd(gen, marks.get(15).getNanoTime()), + Mark.taskEnd(gen, marks.get(16).getNanoTime(), "task4"), Mark.tag(gen, tag3.tagName, tag3.tagId), - Mark.taskEnd(gen, marks.get(15).getNanoTime(), "task3"), + Mark.taskEnd(gen, marks.get(18).getNanoTime(), "task3"), Mark.tag(gen, tag2.tagName, tag2.tagId), - Mark.taskEnd(gen, marks.get(17).getNanoTime(), "task2"), + Mark.taskEnd(gen, marks.get(20).getNanoTime(), "task2"), Mark.tag(gen, tag1.tagName, tag1.tagId), - Mark.taskEnd(gen, marks.get(19).getNanoTime(), "task1"), - Mark.taskStart(gen, marks.get(20).getNanoTime(), "task6"), - Mark.taskStart(gen, marks.get(21).getNanoTime(), "task7"), - Mark.taskEnd(gen, marks.get(22).getNanoTime()), - Mark.taskEnd(gen, marks.get(23).getNanoTime())); + Mark.taskEnd(gen, marks.get(22).getNanoTime(), "task1"), + Mark.taskStart(gen, marks.get(23).getNanoTime(), "task6"), + Mark.taskStart(gen, marks.get(24).getNanoTime(), "task7"), + Mark.taskEnd(gen, marks.get(25).getNanoTime()), + Mark.taskEnd(gen, marks.get(26).getNanoTime())); assertEquals(expected, marks); } @@ -162,7 +168,8 @@ public void attachTag_nullFunctionFailsSilently() { Storage.resetForThread(); PerfMark.setEnabled(true); - PerfMark.attachTag("name", "extra2", null); + StringFunction nullFunction = null; + PerfMark.attachTag("name", "extra2", nullFunction); List marks = Storage.readForTest(); Truth.assertThat(marks).hasSize(1); diff --git a/impl/src/main/java/io/perfmark/impl/SecretPerfMarkImpl.java b/impl/src/main/java/io/perfmark/impl/SecretPerfMarkImpl.java index 173572d6..4967b1a6 100644 --- a/impl/src/main/java/io/perfmark/impl/SecretPerfMarkImpl.java +++ b/impl/src/main/java/io/perfmark/impl/SecretPerfMarkImpl.java @@ -21,6 +21,9 @@ import io.perfmark.StringFunction; import io.perfmark.Tag; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Function; +import java.util.function.ToIntFunction; +import java.util.function.ToLongFunction; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; @@ -251,8 +254,16 @@ protected void startTask(String taskName, String subTaskName) { markRecorder.start(gen, taskName, subTaskName); } - @Override + /** + * This method is needed to work with old version of perfmark-api. + */ protected void startTask(T taskNameObject, StringFunction stringFunction) { + Function function = stringFunction; + startTask(taskNameObject, function); + } + + @Override + protected void startTask(T taskNameObject, Function stringFunction) { final long gen = getGen(); if (!isEnabled(gen)) { return; @@ -349,9 +360,18 @@ protected void attachTag(String tagName, String tagValue) { markRecorder.attachKeyedTag(gen, tagName, tagValue); } + /** + * This method is needed to work with old version of perfmark-api. + */ + public void attachTag( + String tagName, T tagObject, StringFunction stringFunction) { + Function function = stringFunction; + attachTag(tagName, tagObject, function); + } + @Override protected void attachTag( - String tagName, T tagObject, StringFunction stringFunction) { + String tagName, T tagObject, Function stringFunction) { final long gen = getGen(); if (!isEnabled(gen)) { return; @@ -360,8 +380,30 @@ protected void attachTag( markRecorder.attachKeyedTag(gen, tagName, tagValue); } + @Override + protected void attachTag( + String tagName, T tagObject, ToIntFunction intFunction) { + final long gen = getGen(); + if (!isEnabled(gen)) { + return; + } + long tagValue = deriveTagValue(tagName, tagObject, intFunction); + markRecorder.attachKeyedTag(gen, tagName, tagValue); + } + + @Override + protected void attachTag( + String tagName, T tagObject, ToLongFunction longFunction) { + final long gen = getGen(); + if (!isEnabled(gen)) { + return; + } + long tagValue = deriveTagValue(tagName, tagObject, longFunction); + markRecorder.attachKeyedTag(gen, tagName, tagValue); + } + static String deriveTagValue( - String tagName, T tagObject, StringFunction stringFunction) { + String tagName, T tagObject, Function stringFunction) { try { return stringFunction.apply(tagObject); } catch (Throwable t) { @@ -370,7 +412,28 @@ static String deriveTagValue( } } - static String deriveTaskValue(T taskNameObject, StringFunction stringFunction) { + static long deriveTagValue( + String tagName, T tagNameObject, ToIntFunction intFunction) { + try { + // implicit cast + return intFunction.applyAsInt(tagNameObject); + } catch (Throwable t) { + handleTagValueFailure(tagName, tagNameObject, intFunction, t); + return Mark.NO_TAG_ID; + } + } + + static long deriveTagValue( + String tagName, T tagNameObject, ToLongFunction longFunction) { + try { + return longFunction.applyAsLong(tagNameObject); + } catch (Throwable t) { + handleTagValueFailure(tagName, tagNameObject, longFunction, t); + return Mark.NO_TAG_ID; + } + } + + static String deriveTaskValue(T taskNameObject, Function stringFunction) { try { return stringFunction.apply(taskNameObject); } catch (Throwable t) { @@ -380,7 +443,7 @@ static String deriveTaskValue(T taskNameObject, StringFunction st } static void handleTagValueFailure( - String tagName, T tagObject, StringFunction stringFunction, Throwable cause) { + String tagName, T tagObject, Object stringFunction, Throwable cause) { if (logger == null) { return; } @@ -407,7 +470,7 @@ static void handleTagValueFailure( } static void handleTaskNameFailure( - T taskNameObject, StringFunction stringFunction, Throwable cause) { + T taskNameObject, Object function, Throwable cause) { if (logger == null) { return; } @@ -416,8 +479,8 @@ static void handleTaskNameFailure( if (localLogger.isLoggable(Level.FINE)) { LogRecord lr = new LogRecord( - Level.FINE, "PerfMark.startTask failed: taskObject={0}, stringFunction={1}"); - lr.setParameters(new Object[] {taskNameObject, stringFunction}); + Level.FINE, "PerfMark.startTask failed: taskObject={0}, function={1}"); + lr.setParameters(new Object[] {taskNameObject, function}); lr.setThrown(cause); localLogger.log(lr); }