PackStatistics.java

  1. /*
  2.  * Copyright (C) 2015, Google Inc.
  3.  *
  4.  * This program and the accompanying materials are made available
  5.  * under the terms of the Eclipse Distribution License v1.0 which
  6.  * accompanies this distribution, is reproduced below, and is
  7.  * available at http://www.eclipse.org/org/documents/edl-v10.php
  8.  *
  9.  * All rights reserved.
  10.  *
  11.  * Redistribution and use in source and binary forms, with or
  12.  * without modification, are permitted provided that the following
  13.  * conditions are met:
  14.  *
  15.  * - Redistributions of source code must retain the above copyright
  16.  *   notice, this list of conditions and the following disclaimer.
  17.  *
  18.  * - Redistributions in binary form must reproduce the above
  19.  *   copyright notice, this list of conditions and the following
  20.  *   disclaimer in the documentation and/or other materials provided
  21.  *   with the distribution.
  22.  *
  23.  * - Neither the name of the Eclipse Foundation, Inc. nor the
  24.  *   names of its contributors may be used to endorse or promote
  25.  *   products derived from this software without specific prior
  26.  *   written permission.
  27.  *
  28.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  29.  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  30.  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  31.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  32.  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  33.  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  34.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  35.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  36.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  37.  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  38.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  39.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  40.  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  41.  */

  42. package org.eclipse.jgit.storage.pack;

  43. import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
  44. import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
  45. import static org.eclipse.jgit.lib.Constants.OBJ_TAG;
  46. import static org.eclipse.jgit.lib.Constants.OBJ_TREE;

  47. import java.text.MessageFormat;
  48. import java.util.HashMap;
  49. import java.util.List;
  50. import java.util.Map;
  51. import java.util.Set;

  52. import org.eclipse.jgit.internal.JGitText;
  53. import org.eclipse.jgit.internal.storage.pack.CachedPack;
  54. import org.eclipse.jgit.lib.ObjectId;

  55. /**
  56.  * Statistics about {@link org.eclipse.jgit.internal.storage.pack.PackWriter}
  57.  * pack creation.
  58.  *
  59.  * @since 4.1
  60.  */
  61. public class PackStatistics {
  62.     /**
  63.      * Statistics about a single type of object (commits, tags, trees and
  64.      * blobs).
  65.      */
  66.     public static class ObjectType {
  67.         /**
  68.          * POJO for accumulating the ObjectType statistics.
  69.          */
  70.         public static class Accumulator {
  71.             /** Count of objects of this type. */
  72.             public long cntObjects;

  73.             /** Count of deltas of this type. */
  74.             public long cntDeltas;

  75.             /** Count of reused objects of this type. */
  76.             public long reusedObjects;

  77.             /** Count of reused deltas of this type. */
  78.             public long reusedDeltas;

  79.             /** Count of bytes for all objects of this type. */
  80.             public long bytes;

  81.             /** Count of delta bytes for objects of this type. */
  82.             public long deltaBytes;
  83.         }

  84.         private ObjectType.Accumulator objectType;

  85.         /**
  86.          * Creates a new {@link ObjectType} object from the accumulator.
  87.          *
  88.          * @param accumulator
  89.          *            the accumulator of the statistics
  90.          */
  91.         public ObjectType(ObjectType.Accumulator accumulator) {
  92.             /*
  93.              * For efficiency this wraps and serves up the Accumulator object
  94.              * rather than making a deep clone. Normal usage of PackWriter is to
  95.              * create a single pack/index/bitmap and only call getStatistics()
  96.              * after all work is complete.
  97.              */
  98.             objectType = accumulator;
  99.         }

  100.         /**
  101.          * @return total number of objects output. This total includes the value
  102.          *         of {@link #getDeltas()}.
  103.          */
  104.         public long getObjects() {
  105.             return objectType.cntObjects;
  106.         }

  107.         /**
  108.          * @return total number of deltas output. This may be lower than the
  109.          *         actual number of deltas if a cached pack was reused.
  110.          */
  111.         public long getDeltas() {
  112.             return objectType.cntDeltas;
  113.         }

  114.         /**
  115.          * @return number of objects whose existing representation was reused in
  116.          *         the output. This count includes {@link #getReusedDeltas()}.
  117.          */
  118.         public long getReusedObjects() {
  119.             return objectType.reusedObjects;
  120.         }

  121.         /**
  122.          * @return number of deltas whose existing representation was reused in
  123.          *         the output, as their base object was also output or was
  124.          *         assumed present for a thin pack. This may be lower than the
  125.          *         actual number of reused deltas if a cached pack was reused.
  126.          */
  127.         public long getReusedDeltas() {
  128.             return objectType.reusedDeltas;
  129.         }

  130.         /**
  131.          * @return total number of bytes written. This size includes the object
  132.          *         headers as well as the compressed data. This size also
  133.          *         includes all of {@link #getDeltaBytes()}.
  134.          */
  135.         public long getBytes() {
  136.             return objectType.bytes;
  137.         }

  138.         /**
  139.          * @return number of delta bytes written. This size includes the object
  140.          *         headers for the delta objects.
  141.          */
  142.         public long getDeltaBytes() {
  143.             return objectType.deltaBytes;
  144.         }
  145.     }

  146.     /**
  147.      * POJO for accumulating the statistics.
  148.      */
  149.     public static class Accumulator {
  150.         /**
  151.          * The count of references in the ref advertisement.
  152.          *
  153.          * @since 4.11
  154.          */
  155.         public long advertised;

  156.         /**
  157.          * The count of client wants.
  158.          *
  159.          * @since 4.11
  160.          */
  161.         public long wants;

  162.         /**
  163.          * The count of client haves.
  164.          *
  165.          * @since 4.11
  166.          */
  167.         public long haves;

  168.         /**
  169.          * Time in ms spent in the negotiation phase. For non-bidirectional
  170.          * transports (e.g., HTTP), this is only for the final request that
  171.          * sends back the pack file.
  172.          *
  173.          * @since 4.11
  174.          */
  175.         public long timeNegotiating;

  176.         /** The set of objects to be included in the pack. */
  177.         public Set<ObjectId> interestingObjects;

  178.         /** The set of objects to be excluded from the pack. */
  179.         public Set<ObjectId> uninterestingObjects;

  180.         /** The set of shallow commits on the client. */
  181.         public Set<ObjectId> clientShallowCommits;

  182.         /** The collection of reused packs in the upload. */
  183.         public List<CachedPack> reusedPacks;

  184.         /** Commits with no parents. */
  185.         public Set<ObjectId> rootCommits;

  186.         /** If a shallow pack, the depth in commits. */
  187.         public int depth;

  188.         /**
  189.          * The count of objects in the pack that went through the delta search
  190.          * process in order to find a potential delta base.
  191.          */
  192.         public int deltaSearchNonEdgeObjects;

  193.         /**
  194.          * The count of objects in the pack that went through delta base search
  195.          * and found a suitable base. This is a subset of
  196.          * deltaSearchNonEdgeObjects.
  197.          */
  198.         public int deltasFound;

  199.         /** The total count of objects in the pack. */
  200.         public long totalObjects;

  201.         /**
  202.          * The count of objects that needed to be discovered through an object
  203.          * walk because they were not found in bitmap indices.
  204.          */
  205.         public long bitmapIndexMisses;

  206.         /** The total count of deltas output. */
  207.         public long totalDeltas;

  208.         /** The count of reused objects in the pack. */
  209.         public long reusedObjects;

  210.         /** The count of reused deltas in the pack. */
  211.         public long reusedDeltas;

  212.         /** The count of total bytes in the pack. */
  213.         public long totalBytes;

  214.         /** The size of the thin pack in bytes, if a thin pack was generated. */
  215.         public long thinPackBytes;

  216.         /** Time in ms spent counting the objects that will go into the pack. */
  217.         public long timeCounting;

  218.         /** Time in ms spent searching for objects to reuse. */
  219.         public long timeSearchingForReuse;

  220.         /** Time in ms spent searching for sizes of objects. */
  221.         public long timeSearchingForSizes;

  222.         /** Time in ms spent compressing the pack. */
  223.         public long timeCompressing;

  224.         /** Time in ms spent writing the pack. */
  225.         public long timeWriting;

  226.         /** Number of trees traversed in the walk when writing the pack.
  227.          * @since 5.4*/
  228.         public long treesTraversed;

  229.         /**
  230.          * Statistics about each object type in the pack (commits, tags, trees
  231.          * and blobs.)
  232.          */
  233.         public ObjectType.Accumulator[] objectTypes;

  234.         {
  235.             objectTypes = new ObjectType.Accumulator[5];
  236.             objectTypes[OBJ_COMMIT] = new ObjectType.Accumulator();
  237.             objectTypes[OBJ_TREE] = new ObjectType.Accumulator();
  238.             objectTypes[OBJ_BLOB] = new ObjectType.Accumulator();
  239.             objectTypes[OBJ_TAG] = new ObjectType.Accumulator();
  240.         }
  241.     }

  242.     private Accumulator statistics;

  243.     /**
  244.      * Creates a new {@link org.eclipse.jgit.storage.pack.PackStatistics} object
  245.      * from the accumulator.
  246.      *
  247.      * @param accumulator
  248.      *            the accumulator of the statistics
  249.      */
  250.     public PackStatistics(Accumulator accumulator) {
  251.         /*
  252.          * For efficiency this wraps and serves up the Accumulator object rather
  253.          * than making a deep clone. Normal usage of PackWriter is to create a
  254.          * single pack/index/bitmap and only call getStatistics() after all work
  255.          * is complete.
  256.          */
  257.         statistics = accumulator;
  258.     }

  259.     /**
  260.      * Get the count of references in the ref advertisement.
  261.      *
  262.      * @return count of refs in the ref advertisement.
  263.      * @since 4.11
  264.      */
  265.     public long getAdvertised() {
  266.         return statistics.advertised;
  267.     }

  268.     /**
  269.      * Get the count of client wants.
  270.      *
  271.      * @return count of client wants.
  272.      * @since 4.11
  273.      */
  274.     public long getWants() {
  275.         return statistics.wants;
  276.     }

  277.     /**
  278.      * Get the count of client haves.
  279.      *
  280.      * @return count of client haves.
  281.      * @since 4.11
  282.      */
  283.     public long getHaves() {
  284.         return statistics.haves;
  285.     }

  286.     /**
  287.      * Time in ms spent in the negotiation phase. For non-bidirectional
  288.      * transports (e.g., HTTP), this is only for the final request that sends
  289.      * back the pack file.
  290.      *
  291.      * @return time for ref advertisement in ms.
  292.      * @since 4.11
  293.      */
  294.     public long getTimeNegotiating() {
  295.         return statistics.timeNegotiating;
  296.     }

  297.     /**
  298.      * Get unmodifiable collection of objects to be included in the pack.
  299.      *
  300.      * @return unmodifiable collection of objects to be included in the pack.
  301.      *         May be {@code null} if the pack was hand-crafted in a unit test.
  302.      */
  303.     public Set<ObjectId> getInterestingObjects() {
  304.         return statistics.interestingObjects;
  305.     }

  306.     /**
  307.      * Get unmodifiable collection of objects that should be excluded from the
  308.      * pack
  309.      *
  310.      * @return unmodifiable collection of objects that should be excluded from
  311.      *         the pack, as the peer that will receive the pack already has
  312.      *         these objects.
  313.      */
  314.     public Set<ObjectId> getUninterestingObjects() {
  315.         return statistics.uninterestingObjects;
  316.     }

  317.     /**
  318.      * Get unmodifiable collection of objects that were shallow commits on the
  319.      * client.
  320.      *
  321.      * @return unmodifiable collection of objects that were shallow commits on
  322.      *         the client.
  323.      */
  324.     public Set<ObjectId> getClientShallowCommits() {
  325.         return statistics.clientShallowCommits;
  326.     }

  327.     /**
  328.      * Get unmodifiable list of the cached packs that were reused in the output
  329.      *
  330.      * @return unmodifiable list of the cached packs that were reused in the
  331.      *         output, if any were selected for reuse.
  332.      */
  333.     public List<CachedPack> getReusedPacks() {
  334.         return statistics.reusedPacks;
  335.     }

  336.     /**
  337.      * Get unmodifiable collection of the root commits of the history.
  338.      *
  339.      * @return unmodifiable collection of the root commits of the history.
  340.      */
  341.     public Set<ObjectId> getRootCommits() {
  342.         return statistics.rootCommits;
  343.     }

  344.     /**
  345.      * Get number of objects in the output pack that went through the delta
  346.      * search process in order to find a potential delta base.
  347.      *
  348.      * @return number of objects in the output pack that went through the delta
  349.      *         search process in order to find a potential delta base.
  350.      */
  351.     public int getDeltaSearchNonEdgeObjects() {
  352.         return statistics.deltaSearchNonEdgeObjects;
  353.     }

  354.     /**
  355.      * Get number of objects in the output pack that went through delta base
  356.      * search and found a suitable base.
  357.      *
  358.      * @return number of objects in the output pack that went through delta base
  359.      *         search and found a suitable base. This is a subset of
  360.      *         {@link #getDeltaSearchNonEdgeObjects()}.
  361.      */
  362.     public int getDeltasFound() {
  363.         return statistics.deltasFound;
  364.     }

  365.     /**
  366.      * Get total number of objects output.
  367.      *
  368.      * @return total number of objects output. This total includes the value of
  369.      *         {@link #getTotalDeltas()}.
  370.      */
  371.     public long getTotalObjects() {
  372.         return statistics.totalObjects;
  373.     }

  374.     /**
  375.      * Get the count of objects that needed to be discovered through an object
  376.      * walk because they were not found in bitmap indices.
  377.      *
  378.      * @return the count of objects that needed to be discovered through an
  379.      *         object walk because they were not found in bitmap indices.
  380.      *         Returns -1 if no bitmap indices were found.
  381.      */
  382.     public long getBitmapIndexMisses() {
  383.         return statistics.bitmapIndexMisses;
  384.     }

  385.     /**
  386.      * Get total number of deltas output.
  387.      *
  388.      * @return total number of deltas output. This may be lower than the actual
  389.      *         number of deltas if a cached pack was reused.
  390.      */
  391.     public long getTotalDeltas() {
  392.         return statistics.totalDeltas;
  393.     }

  394.     /**
  395.      * Get number of objects whose existing representation was reused in the
  396.      * output.
  397.      *
  398.      * @return number of objects whose existing representation was reused in the
  399.      *         output. This count includes {@link #getReusedDeltas()}.
  400.      */
  401.     public long getReusedObjects() {
  402.         return statistics.reusedObjects;
  403.     }

  404.     /**
  405.      * Get number of deltas whose existing representation was reused in the
  406.      * output.
  407.      *
  408.      * @return number of deltas whose existing representation was reused in the
  409.      *         output, as their base object was also output or was assumed
  410.      *         present for a thin pack. This may be lower than the actual number
  411.      *         of reused deltas if a cached pack was reused.
  412.      */
  413.     public long getReusedDeltas() {
  414.         return statistics.reusedDeltas;
  415.     }

  416.     /**
  417.      * Get total number of bytes written.
  418.      *
  419.      * @return total number of bytes written. This size includes the pack
  420.      *         header, trailer, thin pack, and reused cached pack(s).
  421.      */
  422.     public long getTotalBytes() {
  423.         return statistics.totalBytes;
  424.     }

  425.     /**
  426.      * Get size of the thin pack in bytes.
  427.      *
  428.      * @return size of the thin pack in bytes, if a thin pack was generated. A
  429.      *         thin pack is created when the client already has objects and some
  430.      *         deltas are created against those objects, or if a cached pack is
  431.      *         being used and some deltas will reference objects in the cached
  432.      *         pack. This size does not include the pack header or trailer.
  433.      */
  434.     public long getThinPackBytes() {
  435.         return statistics.thinPackBytes;
  436.     }

  437.     /**
  438.      * Get information about this type of object in the pack.
  439.      *
  440.      * @param typeCode
  441.      *            object type code, e.g. OBJ_COMMIT or OBJ_TREE.
  442.      * @return information about this type of object in the pack.
  443.      */
  444.     public ObjectType byObjectType(int typeCode) {
  445.         return new ObjectType(statistics.objectTypes[typeCode]);
  446.     }

  447.     /**
  448.      * Whether the resulting pack file was a shallow pack.
  449.      *
  450.      * @return {@code true} if the resulting pack file was a shallow pack.
  451.      */
  452.     public boolean isShallow() {
  453.         return statistics.depth > 0;
  454.     }

  455.     /**
  456.      * Get depth (in commits) the pack includes if shallow.
  457.      *
  458.      * @return depth (in commits) the pack includes if shallow.
  459.      */
  460.     public int getDepth() {
  461.         return statistics.depth;
  462.     }

  463.     /**
  464.      * Get time in milliseconds spent enumerating the objects that need to be
  465.      * included in the output.
  466.      *
  467.      * @return time in milliseconds spent enumerating the objects that need to
  468.      *         be included in the output. This time includes any restarts that
  469.      *         occur when a cached pack is selected for reuse.
  470.      */
  471.     public long getTimeCounting() {
  472.         return statistics.timeCounting;
  473.     }

  474.     /**
  475.      * Get time in milliseconds spent matching existing representations against
  476.      * objects that will be transmitted.
  477.      *
  478.      * @return time in milliseconds spent matching existing representations
  479.      *         against objects that will be transmitted, or that the client can
  480.      *         be assumed to already have.
  481.      */
  482.     public long getTimeSearchingForReuse() {
  483.         return statistics.timeSearchingForReuse;
  484.     }

  485.     /**
  486.      * Get time in milliseconds spent finding the sizes of all objects that will
  487.      * enter the delta compression search window.
  488.      *
  489.      * @return time in milliseconds spent finding the sizes of all objects that
  490.      *         will enter the delta compression search window. The sizes need to
  491.      *         be known to better match similar objects together and improve
  492.      *         delta compression ratios.
  493.      */
  494.     public long getTimeSearchingForSizes() {
  495.         return statistics.timeSearchingForSizes;
  496.     }

  497.     /**
  498.      * Get time in milliseconds spent on delta compression.
  499.      *
  500.      * @return time in milliseconds spent on delta compression. This is observed
  501.      *         wall-clock time and does not accurately track CPU time used when
  502.      *         multiple threads were used to perform the delta compression.
  503.      */
  504.     public long getTimeCompressing() {
  505.         return statistics.timeCompressing;
  506.     }

  507.     /**
  508.      * Get time in milliseconds spent writing the pack output, from start of
  509.      * header until end of trailer.
  510.      *
  511.      * @return time in milliseconds spent writing the pack output, from start of
  512.      *         header until end of trailer. The transfer speed can be
  513.      *         approximated by dividing {@link #getTotalBytes()} by this value.
  514.      */
  515.     public long getTimeWriting() {
  516.         return statistics.timeWriting;
  517.     }

  518.     /**
  519.      * @return number of trees traversed in the walk when writing the pack.
  520.      * @since 5.4
  521.      */
  522.     public long getTreesTraversed() {
  523.         return statistics.treesTraversed;
  524.     }

  525.     /**
  526.      * Get total time spent processing this pack.
  527.      *
  528.      * @return total time spent processing this pack.
  529.      */
  530.     public long getTimeTotal() {
  531.         return statistics.timeCounting + statistics.timeSearchingForReuse
  532.                 + statistics.timeSearchingForSizes + statistics.timeCompressing
  533.                 + statistics.timeWriting;
  534.     }

  535.     /**
  536.      * Get the average output speed in terms of bytes-per-second.
  537.      *
  538.      * @return the average output speed in terms of bytes-per-second.
  539.      *         {@code getTotalBytes() / (getTimeWriting() / 1000.0)}.
  540.      */
  541.     public double getTransferRate() {
  542.         return getTotalBytes() / (getTimeWriting() / 1000.0);
  543.     }

  544.     /**
  545.      * Get formatted message string for display to clients.
  546.      *
  547.      * @return formatted message string for display to clients.
  548.      */
  549.     public String getMessage() {
  550.         return MessageFormat.format(JGitText.get().packWriterStatistics,
  551.                 Long.valueOf(statistics.totalObjects),
  552.                 Long.valueOf(statistics.totalDeltas),
  553.                 Long.valueOf(statistics.reusedObjects),
  554.                 Long.valueOf(statistics.reusedDeltas));
  555.     }

  556.     /**
  557.      * Get a map containing ObjectType statistics.
  558.      *
  559.      * @return a map containing ObjectType statistics.
  560.      */
  561.     public Map<Integer, ObjectType> getObjectTypes() {
  562.         HashMap<Integer, ObjectType> map = new HashMap<>();
  563.         map.put(Integer.valueOf(OBJ_BLOB), new ObjectType(
  564.                 statistics.objectTypes[OBJ_BLOB]));
  565.         map.put(Integer.valueOf(OBJ_COMMIT), new ObjectType(
  566.                 statistics.objectTypes[OBJ_COMMIT]));
  567.         map.put(Integer.valueOf(OBJ_TAG), new ObjectType(
  568.                 statistics.objectTypes[OBJ_TAG]));
  569.         map.put(Integer.valueOf(OBJ_TREE), new ObjectType(
  570.                 statistics.objectTypes[OBJ_TREE]));
  571.         return map;
  572.     }
  573. }