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  private final Map<String, Long> m_epochs = new HashMap<>(); // microseconds
026
027  /** Tracer constructor. */
028  public Tracer() {
029    resetTimer();
030  }
031
032  /** Clears all epochs. */
033  public void clearEpochs() {
034    m_epochs.clear();
035    resetTimer();
036  }
037
038  /** Restarts the epoch timer. */
039  public final void resetTimer() {
040    m_startTime = RobotController.getFPGATime();
041  }
042
043  /**
044   * Adds time since last epoch to the list printed by printEpochs().
045   *
046   * <p>Epochs are a way to partition the time elapsed so that when overruns occur, one can
047   * determine which parts of an operation consumed the most time.
048   *
049   * <p>This should be called immediately after execution has finished, with a call to this method
050   * or {@link #resetTimer()} before execution.
051   *
052   * @param epochName The name to associate with the epoch.
053   */
054  public void addEpoch(String epochName) {
055    long currentTime = RobotController.getFPGATime();
056    m_epochs.put(epochName, currentTime - m_startTime);
057    m_startTime = currentTime;
058  }
059
060  /** Prints list of epochs added so far and their times to the DriverStation. */
061  public void printEpochs() {
062    printEpochs(out -> DriverStation.reportWarning(out, false));
063  }
064
065  /**
066   * Prints list of epochs added so far and their times to the entered String consumer.
067   *
068   * <p>This overload can be useful for logging to a file, etc.
069   *
070   * @param output the stream that the output is sent to
071   */
072  public void printEpochs(Consumer<String> output) {
073    long now = RobotController.getFPGATime();
074    if (now - m_lastEpochsPrintTime > kMinPrintPeriod) {
075      StringBuilder sb = new StringBuilder();
076      m_lastEpochsPrintTime = now;
077      m_epochs.forEach(
078          (key, value) -> sb.append(String.format("\t%s: %.6fs\n", key, value / 1.0e6)));
079      if (sb.length() > 0) {
080        output.accept(sb.toString());
081      }
082    }
083  }
084}