/* * Copyright 2016-present Facebook, Inc. * * 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.facebook.buck.util.perf; import com.facebook.buck.event.AbstractBuckEvent; import com.facebook.buck.event.BuckEventBus; import com.facebook.buck.event.EventKey; import com.facebook.buck.log.GlobalStateManager; import com.facebook.buck.log.InvocationInfo; import com.facebook.buck.log.Logger; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.AbstractScheduledService; import com.google.common.util.concurrent.ServiceManager; import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; import java.lang.management.MemoryPoolMXBean; import java.lang.management.MemoryType; import java.util.Map; import java.util.concurrent.TimeUnit; /** Periodically probes for process-wide perf-related metrics. */ public class PerfStatsTracking extends AbstractScheduledService implements AutoCloseable { private final BuckEventBus eventBus; private final ServiceManager serviceManager; private final InvocationInfo invocationInfo; public PerfStatsTracking(BuckEventBus eventBus, InvocationInfo invocationInfo) { this.eventBus = eventBus; this.serviceManager = new ServiceManager(ImmutableList.of(this)); this.invocationInfo = invocationInfo; serviceManager.startAsync(); } public void probeMemory() { long freeMemoryBytes = Runtime.getRuntime().freeMemory(); long totalMemoryBytes = Runtime.getRuntime().totalMemory(); long maxMemoryBytes = Runtime.getRuntime().maxMemory(); long totalGcTimeMs = 0; for (GarbageCollectorMXBean gcMxBean : ManagementFactory.getGarbageCollectorMXBeans()) { long collectionTimeMs = gcMxBean.getCollectionTime(); if (collectionTimeMs == -1) { // Gc collection time is not supported on this JVM. totalGcTimeMs = -1; break; } totalGcTimeMs += collectionTimeMs; } ImmutableMap.Builder<String, Long> currentMemoryBytesUsageByPool = ImmutableMap.builder(); for (MemoryPoolMXBean memoryPoolBean : ManagementFactory.getMemoryPoolMXBeans()) { String name = memoryPoolBean.getName(); MemoryType type = memoryPoolBean.getType(); long currentlyUsedBytes = memoryPoolBean.getUsage().getUsed(); currentMemoryBytesUsageByPool.put(name + "(" + type + ")", currentlyUsedBytes); } eventBus.post( new MemoryPerfStatsEvent( freeMemoryBytes, totalMemoryBytes, maxMemoryBytes, totalGcTimeMs, currentMemoryBytesUsageByPool.build())); } @Override protected void runOneIteration() throws Exception { try { GlobalStateManager.singleton() .getThreadToCommandRegister() .register(Thread.currentThread().getId(), invocationInfo.getCommandId()); probeMemory(); } catch (Exception e) { Logger.get(PerfStatsTracking.class).error(e); throw e; } } @Override protected Scheduler scheduler() { return Scheduler.newFixedRateSchedule(1L, 1L, TimeUnit.SECONDS); } @Override public void close() { serviceManager.stopAsync(); } public static class PerfStatsEvent extends AbstractBuckEvent { protected PerfStatsEvent() { super(EventKey.unique()); } @Override protected String getValueString() { return ""; } @Override public String getEventName() { return ""; } } public static class MemoryPerfStatsEvent extends PerfStatsEvent { private final long freeMemoryBytes; private final long totalMemoryBytes; private final long maxMemoryBytes; private final long timeSpentInGcMs; private final Map<String, Long> currentMemoryBytesUsageByPool; public MemoryPerfStatsEvent( long freeMemoryBytes, long totalMemoryBytes, long maxMemoryBytes, long timeSpentInGcMs, Map<String, Long> currentMemoryBytesUsageByPool) { this.freeMemoryBytes = freeMemoryBytes; this.totalMemoryBytes = totalMemoryBytes; this.maxMemoryBytes = maxMemoryBytes; this.timeSpentInGcMs = timeSpentInGcMs; this.currentMemoryBytesUsageByPool = currentMemoryBytesUsageByPool; } public long getFreeMemoryBytes() { return freeMemoryBytes; } public long getTotalMemoryBytes() { return totalMemoryBytes; } public long getMaxMemoryBytes() { return maxMemoryBytes; } public long getTimeSpentInGcMs() { return timeSpentInGcMs; } public Map<String, Long> getCurrentMemoryBytesUsageByPool() { return currentMemoryBytesUsageByPool; } } }