| // Copyright 2018 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.base.Ascii; |
| import com.google.common.flogger.GoogleLogger; |
| import com.google.common.flogger.LogSites; |
| import com.google.devtools.build.lib.profiler.AutoProfiler.ElapsedTimeReceiver; |
| import java.time.Duration; |
| import java.util.concurrent.TimeUnit; |
| |
| /** Utility for creating {@link AutoProfiler} instances from {@link GoogleLogger} instances. */ |
| public class GoogleAutoProfilerUtils { |
| private static final GoogleLogger selfLogger = GoogleLogger.forEnclosingClass(); |
| private static final String LOGGING_MESSAGE_TEMPLATE = |
| "Spent %d " + Ascii.toLowerCase(TimeUnit.MILLISECONDS.toString()) + " doing %s"; |
| |
| private GoogleAutoProfilerUtils() {} |
| |
| /** |
| * Use {@link #logged(String, Duration)} instead. This should only be called when caller has a |
| * specially configured {@code logger} and cannot use this class's one. |
| */ |
| public static AutoProfiler logged( |
| String description, GoogleLogger logger, Duration minTimeForLogging) { |
| return AutoProfiler.create(makeReceiver(description, logger, minTimeForLogging)); |
| } |
| |
| public static AutoProfiler logged(String description, Duration minTimeForLogging) { |
| return logged(description, selfLogger, minTimeForLogging); |
| } |
| |
| public static AutoProfiler logged(String description) { |
| return AutoProfiler.create(createSimpleLogger(description)); |
| } |
| |
| private static ElapsedTimeReceiver createSimpleLogger(String description) { |
| return elapsedTimeNanos -> log(selfLogger, elapsedTimeNanos, description); |
| } |
| |
| /** |
| * Returns an {@link AutoProfiler} that, when closed, records the elapsed time using {@link |
| * Profiler} and also logs it (in milliseconds) to the default logger. |
| * |
| * <p>The returned {@link AutoProfiler} is thread-safe. |
| */ |
| public static AutoProfiler profiledAndLogged( |
| String taskDescription, ProfilerTask profilerTaskType) { |
| ElapsedTimeReceiver profilingReceiver = |
| new AutoProfiler.ProfilingElapsedTimeReceiver(taskDescription, profilerTaskType); |
| return AutoProfiler.create( |
| new SequencedElapsedTimeReceiver(profilingReceiver, createSimpleLogger(taskDescription))); |
| } |
| |
| /** |
| * Returns an {@link AutoProfiler} that, when closed, will log if the operation exceeds provided |
| * threshold and call the custom {@link ElapsedTimeReceiver} for any duration. |
| */ |
| public static AutoProfiler loggedAndCustomReceiver( |
| String taskDescription, Duration minTimeForLogging, ElapsedTimeReceiver customReceiver) { |
| return AutoProfiler.create( |
| new SequencedElapsedTimeReceiver( |
| makeReceiver(taskDescription, selfLogger, minTimeForLogging), customReceiver)); |
| } |
| |
| private static ElapsedTimeReceiver makeReceiver( |
| String description, GoogleLogger logger, Duration minTimeForLogging) { |
| return new FloggerElapsedTimeReceiver(description, logger, minTimeForLogging); |
| } |
| |
| /** {@link ElapsedTimeReceiver} that will not log a message if the time elapsed is too small. */ |
| private static class FloggerElapsedTimeReceiver implements ElapsedTimeReceiver { |
| // Some classes in Google-internal Blaze use a specially configured logger. When those classes |
| // record elapsed time using this library, they pass their logger in here, which we use instead |
| // of this library's default selfLogger. |
| private final GoogleLogger logger; |
| private final String taskDescription; |
| private final Duration minTimeForLogging; |
| |
| FloggerElapsedTimeReceiver( |
| String taskDescription, GoogleLogger logger, Duration minTimeForLogging) { |
| this.taskDescription = taskDescription; |
| this.minTimeForLogging = minTimeForLogging; |
| this.logger = logger; |
| } |
| |
| @Override |
| public final void accept(long elapsedTimeNanos) { |
| // We avoid eagerly converting elapsedTimeNanos to a Duration to minimize garbage creation. |
| if (elapsedTimeNanos < minTimeForLogging.toNanos()) { |
| return; |
| } |
| log(logger, elapsedTimeNanos, taskDescription); |
| } |
| } |
| |
| private static void log(GoogleLogger logger, long elapsedTimeNanos, String taskDescription) { |
| logger |
| .atInfo() |
| .withInjectedLogSite(LogSites.callerOf(AutoProfiler.class)) |
| .log( |
| LOGGING_MESSAGE_TEMPLATE, |
| // TODO(janakr): confirm that this doesn't show up as a source of garbage. Since it only |
| // happens when we're actually logging, it shouldn't. |
| Duration.ofNanos(elapsedTimeNanos).toMillis(), |
| taskDescription); |
| } |
| |
| private static class SequencedElapsedTimeReceiver implements ElapsedTimeReceiver { |
| private final ElapsedTimeReceiver firstReceiver; |
| private final ElapsedTimeReceiver secondReceiver; |
| |
| private SequencedElapsedTimeReceiver( |
| ElapsedTimeReceiver firstReceiver, ElapsedTimeReceiver secondReceiver) { |
| this.firstReceiver = firstReceiver; |
| this.secondReceiver = secondReceiver; |
| } |
| |
| @Override |
| public void accept(long elapsedTimeNanos) { |
| firstReceiver.accept(elapsedTimeNanos); |
| secondReceiver.accept(elapsedTimeNanos); |
| } |
| } |
| } |