PackStatistics.java
/*
* Copyright (C) 2015, Google Inc.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Distribution License v1.0 which
* accompanies this distribution, is reproduced below, and is
* available at http://www.eclipse.org/org/documents/edl-v10.php
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* - Neither the name of the Eclipse Foundation, Inc. nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.eclipse.jgit.storage.pack;
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
import static org.eclipse.jgit.lib.Constants.OBJ_TAG;
import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.pack.CachedPack;
import org.eclipse.jgit.lib.ObjectId;
/**
* Statistics about {@link org.eclipse.jgit.internal.storage.pack.PackWriter}
* pack creation.
*
* @since 4.1
*/
public class PackStatistics {
/**
* Statistics about a single type of object (commits, tags, trees and
* blobs).
*/
public static class ObjectType {
/**
* POJO for accumulating the ObjectType statistics.
*/
public static class Accumulator {
/** Count of objects of this type. */
public long cntObjects;
/** Count of deltas of this type. */
public long cntDeltas;
/** Count of reused objects of this type. */
public long reusedObjects;
/** Count of reused deltas of this type. */
public long reusedDeltas;
/** Count of bytes for all objects of this type. */
public long bytes;
/** Count of delta bytes for objects of this type. */
public long deltaBytes;
}
private ObjectType.Accumulator objectType;
/**
* Creates a new {@link ObjectType} object from the accumulator.
*
* @param accumulator
* the accumulator of the statistics
*/
public ObjectType(ObjectType.Accumulator accumulator) {
/*
* For efficiency this wraps and serves up the Accumulator object
* rather than making a deep clone. Normal usage of PackWriter is to
* create a single pack/index/bitmap and only call getStatistics()
* after all work is complete.
*/
objectType = accumulator;
}
/**
* @return total number of objects output. This total includes the value
* of {@link #getDeltas()}.
*/
public long getObjects() {
return objectType.cntObjects;
}
/**
* @return total number of deltas output. This may be lower than the
* actual number of deltas if a cached pack was reused.
*/
public long getDeltas() {
return objectType.cntDeltas;
}
/**
* @return number of objects whose existing representation was reused in
* the output. This count includes {@link #getReusedDeltas()}.
*/
public long getReusedObjects() {
return objectType.reusedObjects;
}
/**
* @return number of deltas whose existing representation was reused in
* the output, as their base object was also output or was
* assumed present for a thin pack. This may be lower than the
* actual number of reused deltas if a cached pack was reused.
*/
public long getReusedDeltas() {
return objectType.reusedDeltas;
}
/**
* @return total number of bytes written. This size includes the object
* headers as well as the compressed data. This size also
* includes all of {@link #getDeltaBytes()}.
*/
public long getBytes() {
return objectType.bytes;
}
/**
* @return number of delta bytes written. This size includes the object
* headers for the delta objects.
*/
public long getDeltaBytes() {
return objectType.deltaBytes;
}
}
/**
* POJO for accumulating the statistics.
*/
public static class Accumulator {
/**
* The count of references in the ref advertisement.
*
* @since 4.11
*/
public long advertised;
/**
* The count of client wants.
*
* @since 4.11
*/
public long wants;
/**
* The count of client haves.
*
* @since 4.11
*/
public long haves;
/**
* Time in ms spent in the negotiation phase. For non-bidirectional
* transports (e.g., HTTP), this is only for the final request that
* sends back the pack file.
*
* @since 4.11
*/
public long timeNegotiating;
/** The set of objects to be included in the pack. */
public Set<ObjectId> interestingObjects;
/** The set of objects to be excluded from the pack. */
public Set<ObjectId> uninterestingObjects;
/** The set of shallow commits on the client. */
public Set<ObjectId> clientShallowCommits;
/** The collection of reused packs in the upload. */
public List<CachedPack> reusedPacks;
/** Commits with no parents. */
public Set<ObjectId> rootCommits;
/** If a shallow pack, the depth in commits. */
public int depth;
/**
* The count of objects in the pack that went through the delta search
* process in order to find a potential delta base.
*/
public int deltaSearchNonEdgeObjects;
/**
* The count of objects in the pack that went through delta base search
* and found a suitable base. This is a subset of
* deltaSearchNonEdgeObjects.
*/
public int deltasFound;
/** The total count of objects in the pack. */
public long totalObjects;
/**
* The count of objects that needed to be discovered through an object
* walk because they were not found in bitmap indices.
*/
public long bitmapIndexMisses;
/** The total count of deltas output. */
public long totalDeltas;
/** The count of reused objects in the pack. */
public long reusedObjects;
/** The count of reused deltas in the pack. */
public long reusedDeltas;
/** The count of total bytes in the pack. */
public long totalBytes;
/** The size of the thin pack in bytes, if a thin pack was generated. */
public long thinPackBytes;
/** Time in ms spent counting the objects that will go into the pack. */
public long timeCounting;
/** Time in ms spent searching for objects to reuse. */
public long timeSearchingForReuse;
/** Time in ms spent searching for sizes of objects. */
public long timeSearchingForSizes;
/** Time in ms spent compressing the pack. */
public long timeCompressing;
/** Time in ms spent writing the pack. */
public long timeWriting;
/** Number of trees traversed in the walk when writing the pack.
* @since 5.4*/
public long treesTraversed;
/**
* Statistics about each object type in the pack (commits, tags, trees
* and blobs.)
*/
public ObjectType.Accumulator[] objectTypes;
{
objectTypes = new ObjectType.Accumulator[5];
objectTypes[OBJ_COMMIT] = new ObjectType.Accumulator();
objectTypes[OBJ_TREE] = new ObjectType.Accumulator();
objectTypes[OBJ_BLOB] = new ObjectType.Accumulator();
objectTypes[OBJ_TAG] = new ObjectType.Accumulator();
}
}
private Accumulator statistics;
/**
* Creates a new {@link org.eclipse.jgit.storage.pack.PackStatistics} object
* from the accumulator.
*
* @param accumulator
* the accumulator of the statistics
*/
public PackStatistics(Accumulator accumulator) {
/*
* For efficiency this wraps and serves up the Accumulator object rather
* than making a deep clone. Normal usage of PackWriter is to create a
* single pack/index/bitmap and only call getStatistics() after all work
* is complete.
*/
statistics = accumulator;
}
/**
* Get the count of references in the ref advertisement.
*
* @return count of refs in the ref advertisement.
* @since 4.11
*/
public long getAdvertised() {
return statistics.advertised;
}
/**
* Get the count of client wants.
*
* @return count of client wants.
* @since 4.11
*/
public long getWants() {
return statistics.wants;
}
/**
* Get the count of client haves.
*
* @return count of client haves.
* @since 4.11
*/
public long getHaves() {
return statistics.haves;
}
/**
* Time in ms spent in the negotiation phase. For non-bidirectional
* transports (e.g., HTTP), this is only for the final request that sends
* back the pack file.
*
* @return time for ref advertisement in ms.
* @since 4.11
*/
public long getTimeNegotiating() {
return statistics.timeNegotiating;
}
/**
* Get unmodifiable collection of objects to be included in the pack.
*
* @return unmodifiable collection of objects to be included in the pack.
* May be {@code null} if the pack was hand-crafted in a unit test.
*/
public Set<ObjectId> getInterestingObjects() {
return statistics.interestingObjects;
}
/**
* Get unmodifiable collection of objects that should be excluded from the
* pack
*
* @return unmodifiable collection of objects that should be excluded from
* the pack, as the peer that will receive the pack already has
* these objects.
*/
public Set<ObjectId> getUninterestingObjects() {
return statistics.uninterestingObjects;
}
/**
* Get unmodifiable collection of objects that were shallow commits on the
* client.
*
* @return unmodifiable collection of objects that were shallow commits on
* the client.
*/
public Set<ObjectId> getClientShallowCommits() {
return statistics.clientShallowCommits;
}
/**
* Get unmodifiable list of the cached packs that were reused in the output
*
* @return unmodifiable list of the cached packs that were reused in the
* output, if any were selected for reuse.
*/
public List<CachedPack> getReusedPacks() {
return statistics.reusedPacks;
}
/**
* Get unmodifiable collection of the root commits of the history.
*
* @return unmodifiable collection of the root commits of the history.
*/
public Set<ObjectId> getRootCommits() {
return statistics.rootCommits;
}
/**
* Get number of objects in the output pack that went through the delta
* search process in order to find a potential delta base.
*
* @return number of objects in the output pack that went through the delta
* search process in order to find a potential delta base.
*/
public int getDeltaSearchNonEdgeObjects() {
return statistics.deltaSearchNonEdgeObjects;
}
/**
* Get number of objects in the output pack that went through delta base
* search and found a suitable base.
*
* @return number of objects in the output pack that went through delta base
* search and found a suitable base. This is a subset of
* {@link #getDeltaSearchNonEdgeObjects()}.
*/
public int getDeltasFound() {
return statistics.deltasFound;
}
/**
* Get total number of objects output.
*
* @return total number of objects output. This total includes the value of
* {@link #getTotalDeltas()}.
*/
public long getTotalObjects() {
return statistics.totalObjects;
}
/**
* Get the count of objects that needed to be discovered through an object
* walk because they were not found in bitmap indices.
*
* @return the count of objects that needed to be discovered through an
* object walk because they were not found in bitmap indices.
* Returns -1 if no bitmap indices were found.
*/
public long getBitmapIndexMisses() {
return statistics.bitmapIndexMisses;
}
/**
* Get total number of deltas output.
*
* @return total number of deltas output. This may be lower than the actual
* number of deltas if a cached pack was reused.
*/
public long getTotalDeltas() {
return statistics.totalDeltas;
}
/**
* Get number of objects whose existing representation was reused in the
* output.
*
* @return number of objects whose existing representation was reused in the
* output. This count includes {@link #getReusedDeltas()}.
*/
public long getReusedObjects() {
return statistics.reusedObjects;
}
/**
* Get number of deltas whose existing representation was reused in the
* output.
*
* @return number of deltas whose existing representation was reused in the
* output, as their base object was also output or was assumed
* present for a thin pack. This may be lower than the actual number
* of reused deltas if a cached pack was reused.
*/
public long getReusedDeltas() {
return statistics.reusedDeltas;
}
/**
* Get total number of bytes written.
*
* @return total number of bytes written. This size includes the pack
* header, trailer, thin pack, and reused cached pack(s).
*/
public long getTotalBytes() {
return statistics.totalBytes;
}
/**
* Get size of the thin pack in bytes.
*
* @return size of the thin pack in bytes, if a thin pack was generated. A
* thin pack is created when the client already has objects and some
* deltas are created against those objects, or if a cached pack is
* being used and some deltas will reference objects in the cached
* pack. This size does not include the pack header or trailer.
*/
public long getThinPackBytes() {
return statistics.thinPackBytes;
}
/**
* Get information about this type of object in the pack.
*
* @param typeCode
* object type code, e.g. OBJ_COMMIT or OBJ_TREE.
* @return information about this type of object in the pack.
*/
public ObjectType byObjectType(int typeCode) {
return new ObjectType(statistics.objectTypes[typeCode]);
}
/**
* Whether the resulting pack file was a shallow pack.
*
* @return {@code true} if the resulting pack file was a shallow pack.
*/
public boolean isShallow() {
return statistics.depth > 0;
}
/**
* Get depth (in commits) the pack includes if shallow.
*
* @return depth (in commits) the pack includes if shallow.
*/
public int getDepth() {
return statistics.depth;
}
/**
* Get time in milliseconds spent enumerating the objects that need to be
* included in the output.
*
* @return time in milliseconds spent enumerating the objects that need to
* be included in the output. This time includes any restarts that
* occur when a cached pack is selected for reuse.
*/
public long getTimeCounting() {
return statistics.timeCounting;
}
/**
* Get time in milliseconds spent matching existing representations against
* objects that will be transmitted.
*
* @return time in milliseconds spent matching existing representations
* against objects that will be transmitted, or that the client can
* be assumed to already have.
*/
public long getTimeSearchingForReuse() {
return statistics.timeSearchingForReuse;
}
/**
* Get time in milliseconds spent finding the sizes of all objects that will
* enter the delta compression search window.
*
* @return time in milliseconds spent finding the sizes of all objects that
* will enter the delta compression search window. The sizes need to
* be known to better match similar objects together and improve
* delta compression ratios.
*/
public long getTimeSearchingForSizes() {
return statistics.timeSearchingForSizes;
}
/**
* Get time in milliseconds spent on delta compression.
*
* @return time in milliseconds spent on delta compression. This is observed
* wall-clock time and does not accurately track CPU time used when
* multiple threads were used to perform the delta compression.
*/
public long getTimeCompressing() {
return statistics.timeCompressing;
}
/**
* Get time in milliseconds spent writing the pack output, from start of
* header until end of trailer.
*
* @return time in milliseconds spent writing the pack output, from start of
* header until end of trailer. The transfer speed can be
* approximated by dividing {@link #getTotalBytes()} by this value.
*/
public long getTimeWriting() {
return statistics.timeWriting;
}
/**
* @return number of trees traversed in the walk when writing the pack.
* @since 5.4
*/
public long getTreesTraversed() {
return statistics.treesTraversed;
}
/**
* Get total time spent processing this pack.
*
* @return total time spent processing this pack.
*/
public long getTimeTotal() {
return statistics.timeCounting + statistics.timeSearchingForReuse
+ statistics.timeSearchingForSizes + statistics.timeCompressing
+ statistics.timeWriting;
}
/**
* Get the average output speed in terms of bytes-per-second.
*
* @return the average output speed in terms of bytes-per-second.
* {@code getTotalBytes() / (getTimeWriting() / 1000.0)}.
*/
public double getTransferRate() {
return getTotalBytes() / (getTimeWriting() / 1000.0);
}
/**
* Get formatted message string for display to clients.
*
* @return formatted message string for display to clients.
*/
public String getMessage() {
return MessageFormat.format(JGitText.get().packWriterStatistics,
Long.valueOf(statistics.totalObjects),
Long.valueOf(statistics.totalDeltas),
Long.valueOf(statistics.reusedObjects),
Long.valueOf(statistics.reusedDeltas));
}
/**
* Get a map containing ObjectType statistics.
*
* @return a map containing ObjectType statistics.
*/
public Map<Integer, ObjectType> getObjectTypes() {
HashMap<Integer, ObjectType> map = new HashMap<>();
map.put(Integer.valueOf(OBJ_BLOB), new ObjectType(
statistics.objectTypes[OBJ_BLOB]));
map.put(Integer.valueOf(OBJ_COMMIT), new ObjectType(
statistics.objectTypes[OBJ_COMMIT]));
map.put(Integer.valueOf(OBJ_TAG), new ObjectType(
statistics.objectTypes[OBJ_TAG]));
map.put(Integer.valueOf(OBJ_TREE), new ObjectType(
statistics.objectTypes[OBJ_TREE]));
return map;
}
}