All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.perfmark.tracewriter.MarkListWalker Maven / Gradle / Ivy

package io.perfmark.tracewriter;

import io.perfmark.impl.Mark;
import io.perfmark.impl.MarkList;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

class MarkListWalker {
  MarkListWalker() {}

  // TODO: make sure the generations dont have any timestamp overlap
  final void walk(List markLists, long nowNanoTime) {
    Map> generationToMarkLists = groupMarkListsByGeneration(markLists);
    for (Map.Entry> entry : generationToMarkLists.entrySet()) {
      enterGeneration(entry.getKey());
      for (MarkList markList : entry.getValue()) {
        enterMarkList(markList.getThreadName(), markList.getThreadId(), markList.getMarkListId());
        Deque fakeStarts = new ArrayDeque<>();
        Deque fakeEnds = new ArrayDeque<>();
        Set unmatchedPairMarks =
            Collections.newSetFromMap(new IdentityHashMap());
        createFakes(fakeStarts, fakeEnds, unmatchedPairMarks, markList.getMarks(), nowNanoTime);
        for (Mark mark : fakeStarts) {
          onTaskStart(mark, true, false);
        }
        for (Mark mark : markList.getMarks()) {
          onRealMark(mark, unmatchedPairMarks);
        }
        for (Mark mark : fakeEnds) {
          onTaskEnd(mark, false, true);
        }
        exitMarkList();
      }
      exitGeneration();
    }
  }

  protected void enterGeneration(long generation) {}

  protected void exitGeneration() {}

  protected void enterMarkList(String threadName, long threadId, long markListId) {}

  protected void exitMarkList() {}

  private void onRealMark(Mark mark, Collection unmatchedPairMarks) {
    switch (mark.getOperation()) {
      case NONE:
        break;
      case TASK_START:
      case TASK_NOTAG_START:
        onTaskStart(mark, false, unmatchedPairMarks.contains(mark));
        return;
      case TASK_END:
      case TASK_NOTAG_END:
        onTaskEnd(mark, unmatchedPairMarks.contains(mark), false);
        return;
      case EVENT:
      case EVENT_NOTAG:
        onEvent(mark);
        return;
      case LINK:
        onLink(mark);
        return;
    }
    throw new AssertionError();
  }

  protected void onTaskStart(Mark mark, boolean unmatchedStart, boolean unmatchedEnd) {}

  protected void onTaskEnd(Mark mark, boolean unmatchedStart, boolean unmatchedEnd) {}

  protected void onLink(Mark mark) {}

  protected void onEvent(Mark mark) {}

  private static Map> groupMarkListsByGeneration(
      List markLists) {
    Map> generationToMarkLists = new TreeMap<>();
    for (MarkList markList : markLists) {
      List marks = markList.getMarks();
      if (marks.isEmpty()) {
        continue;
      }
      Map> generationToMarks = new TreeMap<>();
      for (Mark mark : marks) {
        List groupedMarks = generationToMarks.get(mark.getGeneration());
        if (groupedMarks == null) {
          generationToMarks.put(mark.getGeneration(), groupedMarks = new ArrayList<>());
        }
        groupedMarks.add(mark);
      }
      // note: marklists without any marks are lost here, since they have no generation.
      for (Map.Entry> entry : generationToMarks.entrySet()) {
        List groupedMarkLists = generationToMarkLists.get(entry.getKey());
        if (groupedMarkLists == null) {
          generationToMarkLists.put(entry.getKey(), groupedMarkLists = new ArrayList<>());
        }
        groupedMarkLists.add(markList.toBuilder().setMarks(entry.getValue()).build());
      }
    }
    // TODO: make a defensive copy of this and the sublists
    return generationToMarkLists;
  }

  private static void createFakes(
      Deque fakeStarts,
      Deque fakeEnds,
      Set unmatchedPairMarks,
      List marks,
      long nowNanoTime) {
    final Deque unmatchedMarks = new ArrayDeque<>();
    long[] nanoTimeBounds = new long[2]; // first, last
    nanoTimeBounds[0] = nowNanoTime; // forces each subsequent overwrite to succeed.
    nanoTimeBounds[1] = nowNanoTime;

    loop:
    for (Mark mark : marks) {
      setNanoTimeBounds(nanoTimeBounds, mark);
      switch (mark.getOperation()) {
        case TASK_START:
        case TASK_NOTAG_START:
          unmatchedMarks.addLast(mark);
          continue loop;
        case TASK_END:
        case TASK_NOTAG_END:
          if (!unmatchedMarks.isEmpty()) {
            // TODO: maybe double check the tags and task names match
            unmatchedMarks.removeLast();
          } else {
            fakeStarts.addFirst(createFakeStart(mark, nanoTimeBounds[0]));
            unmatchedPairMarks.add(mark);
          }
          continue loop;
        case EVENT:
        case EVENT_NOTAG:
        case LINK:
          continue loop;
        case NONE:
          break;
      }
      throw new AssertionError();
    }
    for (Mark unmatchedMark : unmatchedMarks) {
      fakeEnds.addFirst(createFakeEnd(unmatchedMark, nanoTimeBounds[1]));
      unmatchedPairMarks.add(unmatchedMark);
    }
    unmatchedMarks.clear();
  }

  private static void setNanoTimeBounds(long[] nanoTimeBounds, Mark mark) {
    switch (mark.getOperation()) {
      case TASK_NOTAG_START:
      case TASK_START:
      case TASK_NOTAG_END:
      case TASK_END:
      case EVENT:
      case EVENT_NOTAG:
        if (mark.getNanoTime() - nanoTimeBounds[0] < 0) {
          nanoTimeBounds[0] = mark.getNanoTime();
        }
        if (mark.getNanoTime() - nanoTimeBounds[1] > 0) {
          nanoTimeBounds[1] = mark.getNanoTime();
        }
        return;
      case LINK:
        return;
      case NONE:
        break;
    }
    throw new AssertionError();
  }

  private static Mark createFakeEnd(Mark start, long lastNanoTime) {
    if (start.getMarker() != null) {
      switch (start.getOperation()) {
        case TASK_START:
          return Mark.create(
              start.getMarker(),
              start.getTagName(),
              start.getTagId(),
              lastNanoTime,
              start.getGeneration(),
              Mark.Operation.TASK_END);
        case TASK_NOTAG_START:
          return Mark.create(
              start.getMarker(),
              Mark.NO_TAG_NAME,
              Mark.NO_TAG_ID,
              lastNanoTime,
              start.getGeneration(),
              Mark.Operation.TASK_NOTAG_END);
        case LINK:
        case NONE:
        case TASK_END:
        case TASK_NOTAG_END:
        case EVENT:
        case EVENT_NOTAG:
          break;
      }
      throw new AssertionError();
    } else {
      String taskName = String.valueOf(start.getTaskName()); // Uses "null" if null.
      switch (start.getOperation()) {
        case TASK_START:
          return Mark.create(
              taskName,
              start.getTagName(),
              start.getTagId(),
              lastNanoTime,
              start.getGeneration(),
              Mark.Operation.TASK_END);
        case TASK_NOTAG_START:
          return Mark.create(
              taskName,
              Mark.NO_TAG_NAME,
              Mark.NO_TAG_ID,
              lastNanoTime,
              start.getGeneration(),
              Mark.Operation.TASK_NOTAG_END);
        case LINK:
        case NONE:
        case TASK_END:
        case TASK_NOTAG_END:
        case EVENT:
        case EVENT_NOTAG:
          break;
      }
      throw new AssertionError();
    }
  }

  private static Mark createFakeStart(Mark end, long firstNanoTime) {
    if (end.getMarker() != null) {
      switch (end.getOperation()) {
        case TASK_END:
          return Mark.create(
              end.getMarker(),
              end.getTagName(),
              end.getTagId(),
              firstNanoTime,
              end.getGeneration(),
              Mark.Operation.TASK_START);
        case TASK_NOTAG_END:
          return Mark.create(
              end.getMarker(),
              Mark.NO_TAG_NAME,
              Mark.NO_TAG_ID,
              firstNanoTime,
              end.getGeneration(),
              Mark.Operation.TASK_NOTAG_START);
        case LINK:
        case NONE:
        case TASK_START:
        case TASK_NOTAG_START:
        case EVENT:
        case EVENT_NOTAG:
          break;
      }
      throw new AssertionError();
    } else {
      String taskName = String.valueOf(end.getTaskName()); // Uses "null" if null.
      switch (end.getOperation()) {
        case TASK_END:
          return Mark.create(
              taskName,
              end.getTagName(),
              end.getTagId(),
              firstNanoTime,
              end.getGeneration(),
              Mark.Operation.TASK_START);
        case TASK_NOTAG_END:
          return Mark.create(
              taskName,
              Mark.NO_TAG_NAME,
              Mark.NO_TAG_ID,
              firstNanoTime,
              end.getGeneration(),
              Mark.Operation.TASK_NOTAG_START);
        case LINK:
        case NONE:
        case TASK_START:
        case TASK_NOTAG_START:
        case EVENT:
        case EVENT_NOTAG:
          break;
      }
      throw new AssertionError();
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy