blob: 387c3d5b26953609a72368c496596124b0f9dbc8 [file] [log] [blame]
// Copyright 2025 The Bazel Authors. All rights reserved.
//
// 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 com.google.devtools.build.lib.profiler;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.devtools.build.lib.clock.Clock;
import com.google.devtools.build.lib.runtime.BlazeService;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.io.IOException;
import java.io.OutputStream;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.Supplier;
/**
* Interface for the Blaze internal profiler. Provides facility to report various Blaze tasks and
* store them (asynchronously) in the file for future analysis.
*
* <p>Implemented as singleton so any caller should use Profiler.instance() to obtain reference.
*
* <p>Internally, profiler uses two data structures - ThreadLocal task stack to track nested tasks
* and single ConcurrentLinkedQueue to gather all completed tasks.
*
* <p>Also, due to the nature of the provided functionality (instrumentation of all Blaze
* components), build.lib.profiler package will be used by almost every other Blaze package, so
* special attention should be paid to avoid any dependencies on the rest of the Blaze code,
* including build.lib.util and build.lib.vfs. This is important because build.lib.util and
* build.lib.vfs contain Profiler invocations and any dependency on those two packages would create
* circular relationship.
*
* @see ProfilerTask enum for recognized task types.
*/
@SuppressWarnings("GoodTime") // This code is very performance sensitive.
public interface TraceProfilerService extends BlazeService {
/** File format enum. */
enum Format {
JSON_TRACE_FILE_FORMAT,
JSON_TRACE_FILE_COMPRESSED_FORMAT
}
/** Returns the nanoTime of the current profiler instance, or -1 if not active. */
long nanoTimeMaybe();
/** Returns true iff profiling is currently enabled. */
boolean isActive();
/**
* Records the beginning of a task as specified, and returns a {@link SilentCloseable} instance
* that ends the task. This lets the system do the work of ending the task, with the compiler
* giving a warning if the returned instance is not closed.
*
* <p>Use of this method allows to support nested task monitoring. For tasks that are known to not
* have any subtasks, logSimpleTask() should be used instead.
*
* <p>Use like this:
*
* <pre>{@code
* try (SilentCloseable c = Profiler.instance().profile(type, "description")) {
* // Your code here.
* }
* }</pre>
*
* @param type predefined task type - see ProfilerTask for available types.
* @param description task description. May be stored until the end of the build.
*/
SilentCloseable profile(ProfilerTask type, String description);
/**
* Version of {@link #profile(ProfilerTask, String)} that avoids creating string unless actually
* profiling.
*/
SilentCloseable profile(ProfilerTask type, Supplier<String> description);
/**
* Records the beginning of a task as specified, and returns a {@link SilentCloseable} instance
* that ends the task. This lets the system do the work of ending the task, with the compiler
* giving a warning if the returned instance is not closed.
*
* <p>Use of this method allows to support nested task monitoring. For tasks that are known to not
* have any subtasks, logSimpleTask() should be used instead.
*
* <p>This is a convenience method that uses {@link ProfilerTask#INFO}.
*
* <p>Use like this:
*
* <pre>{@code
* try (SilentCloseable c = Profiler.instance().profile("description")) {
* // Your code here.
* }
* }</pre>
*
* @param description task description. May be stored until the end of the build.
*/
SilentCloseable profile(String description);
/**
* Used externally to submit simple task (one that does not have any subtasks). Depending on the
* minDuration attribute of the task type, task may be just aggregated into the parent task and
* not stored directly.
*
* @param startTimeNanos task start time (obtained through {@link Profiler#nanoTimeMaybe()})
* @param type task type
* @param description task description. May be stored until the end of the build.
*/
void logSimpleTask(long startTimeNanos, ProfilerTask type, String description);
/**
* Used externally to submit simple task (one that does not have any subtasks). Depending on the
* minDuration attribute of the task type, task may be just aggregated into the parent task and
* not stored directly.
*
* <p>Note that start and stop time must both be acquired from the same clock instance.
*
* @param startTimeNanos task start time
* @param stopTimeNanos task stop time
* @param type task type
* @param description task description. May be stored until the end of the build.
*/
void logSimpleTask(
long startTimeNanos, long stopTimeNanos, ProfilerTask type, String description);
/**
* Used externally to submit simple task (one that does not have any subtasks). Depending on the
* minDuration attribute of the task type, task may be just aggregated into the parent task and
* not stored directly.
*
* @param startTimeNanos task start time (obtained through {@link Profiler#nanoTimeMaybe()})
* @param duration the duration of the task
* @param type task type
* @param description task description. May be stored until the end of the build.
*/
void logSimpleTaskDuration(
long startTimeNanos, Duration duration, ProfilerTask type, String description);
/** Used to log "events" happening at a specific time - tasks with zero duration. */
void logEventAtTime(long atTimeNanos, ProfilerTask type, String description);
/** Used to log "events" - tasks with zero duration. */
void logEvent(ProfilerTask type, String description);
/**
* Enable profiling.
*
* <p>Subsequent calls to beginTask/endTask will be recorded in the provided output stream. Please
* note that stream performance is extremely important and buffered streams should be utilized.
*
* @param profiledTasks which of {@link ProfilerTask}s to track
* @param stream output stream to store profile data. Note: passing unbuffered stream object
* reference may result in significant performance penalties
* @param recordAllDurations iff true, record all tasks regardless of their duration; otherwise
* some tasks may get aggregated if they finished quick enough
* @param clock a {@code BlazeClock.instance()}
* @param execStartTimeNanos execution start time in nanos obtained from {@code clock.nanoTime()}
*/
void start(
Set<ProfilerTask> profiledTasks,
OutputStream stream,
Format format,
String outputBase,
UUID buildID,
boolean recordAllDurations,
Clock clock,
long execStartTimeNanos,
boolean slimProfile,
boolean includePrimaryOutput,
boolean includeTargetLabel,
boolean includeConfiguration,
boolean collectTaskHistograms)
throws IOException;
/**
* Disable profiling and complete profile file creation. Subsequent calls to beginTask/endTask
* will no longer be recorded in the profile.
*/
void stop() throws IOException;
/**
* Clears the records the profiler instance keeps.
*
* <p>Should always be called between a {@link #stop()} and a subsequent {@link #start}.
*/
void clear();
/**
* Returns task histograms. This must be called between calls to {@link #start} and {@link #stop},
* or the returned recorders are all empty. Note that the returned recorders may still be modified
* concurrently (but at least they are thread-safe, so that's good).
*
* <p>The stat recorders are indexed by {@code ProfilerTask#ordinal}. //TODO(b/458037154): Maybe
* make the enums stable.
*/
List<StatRecorder> getTasksHistograms();
/**
* Returns task histograms. This must be called between calls to {@link #start} and {@link #stop},
* or the returned list is empty.
*/
Iterable<SlowTask> getSlowestTasks();
boolean isProfiling(ProfilerTask type);
/** Convenience method to log phase marker tasks. */
void markPhase(ProfilePhase phase) throws InterruptedException;
/**
* Similar to {@link #profile}, but specific to action-related events. Takes an extra argument:
* primaryOutput.
*/
SilentCloseable profileAction(
ProfilerTask type,
String mnemonic,
String description,
String primaryOutput,
String targetLabel,
String configuration);
/**
* Records the end of a task as specified.
*
* @param startTimeNanos task start time
* @param type task type
* @param description task description
*/
void completeTask(long startTimeNanos, ProfilerTask type, String description);
void registerCounterSeriesCollector(CounterSeriesCollector collector);
void unregisterCounterSeriesCollector(CounterSeriesCollector collector);
/** Adds a whole action count series to the writer bypassing histogram and subtask creation. */
void logCounters(
Map<CounterSeriesTask, double[]> counterSeriesMap,
Duration profileStart,
Duration bucketDuration);
Duration getProfileElapsedTime();
Duration getServerProcessCpuTime();
/**
* Profiles a future.
*
* @param future the future to profile
* @param prefix the prefix of the virtual lanes. Similar to the thread name prefix.
* @param type task type
* @param description task description. May be stored until the end of the build.
*/
@CanIgnoreReturnValue
<T> ListenableFuture<T> profileFuture(
ListenableFuture<T> future, String prefix, ProfilerTask type, String description);
/**
* Creates a profiler that can be used to profile async operations of a task.
*
* @param prefix the prefix of the virtual lanes. Similar to the thread name prefix.
* @param description the description of task.
*/
AsyncProfiler profileAsync(String prefix, String description);
/** Creates a time series with the given start time and bucket duration. */
TimeSeries createTimeSeries(Duration startTime, Duration bucketDuration);
}