001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package edu.wpi.first.wpilibj;
006
007import java.util.HashMap;
008import java.util.Map;
009import java.util.function.Consumer;
010
011/**
012 * A class for keeping track of how much time it takes for different parts of code to execute. This
013 * is done with epochs, that are added by calls to {@link #addEpoch(String)}, and can be printed
014 * with a call to {@link #printEpochs()}.
015 *
016 * <p>Epochs are a way to partition the time elapsed so that when overruns occur, one can determine
017 * which parts of an operation consumed the most time.
018 */
019public class Tracer {
020  private static final long kMinPrintPeriod = 1000000; // microseconds
021
022  private long m_lastEpochsPrintTime; // microseconds
023  private long m_startTime; // microseconds
024
025  @SuppressWarnings("PMD.UseConcurrentHashMap")
026  private final Map<String, Long> m_epochs = new HashMap<>(); // microseconds
027
028  /** Tracer constructor. */
029  public Tracer() {
030    resetTimer();
031  }
032
033  /** Clears all epochs. */
034  public void clearEpochs() {
035    m_epochs.clear();
036    resetTimer();
037  }
038
039  /** Restarts the epoch timer. */
040  public void resetTimer() {
041    m_startTime = RobotController.getFPGATime();
042  }
043
044  /**
045   * Adds time since last epoch to the list printed by printEpochs().
046   *
047   * <p>Epochs are a way to partition the time elapsed so that when overruns occur, one can
048   * determine which parts of an operation consumed the most time.
049   *
050   * <p>This should be called immediately after execution has finished, with a call to this method
051   * or {@link #resetTimer()} before execution.
052   *
053   * @param epochName The name to associate with the epoch.
054   */
055  public void addEpoch(String epochName) {
056    long currentTime = RobotController.getFPGATime();
057    m_epochs.put(epochName, currentTime - m_startTime);
058    m_startTime = currentTime;
059  }
060
061  /** Prints list of epochs added so far and their times to the DriverStation. */
062  public void printEpochs() {
063    printEpochs(out -> DriverStation.reportWarning(out, false));
064  }
065
066  /**
067   * Prints list of epochs added so far and their times to the entered String consumer.
068   *
069   * <p>This overload can be useful for logging to a file, etc.
070   *
071   * @param output the stream that the output is sent to
072   */
073  public void printEpochs(Consumer<String> output) {
074    long now = RobotController.getFPGATime();
075    if (now - m_lastEpochsPrintTime > kMinPrintPeriod) {
076      StringBuilder sb = new StringBuilder();
077      m_lastEpochsPrintTime = now;
078      m_epochs.forEach(
079          (key, value) -> {
080            sb.append(String.format("\t%s: %.6fs\n", key, value / 1.0e6));
081          });
082      if (sb.length() > 0) {
083        output.accept(sb.toString());
084      }
085    }
086  }
087}