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.          * The count of wants that were not advertised by the server.
  170.          *
  171.          * @since 5.10
  172.          */
  173.         public long notAdvertisedWants;

  174.         /**
  175.          * Time in ms spent in the negotiation phase. For non-bidirectional
  176.          * transports (e.g., HTTP), this is only for the final request that
  177.          * sends back the pack file.
  178.          *
  179.          * @since 4.11
  180.          */
  181.         public long timeNegotiating;

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

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

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

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

  190.         /** Commits with no parents. */
  191.         public Set<ObjectId> rootCommits;

  192.         /** If a shallow pack, the depth in commits. */
  193.         public int depth;

  194.         /**
  195.          * The count of objects in the pack that went through the delta search
  196.          * process in order to find a potential delta base.
  197.          */
  198.         public int deltaSearchNonEdgeObjects;

  199.         /**
  200.          * The count of objects in the pack that went through delta base search
  201.          * and found a suitable base. This is a subset of
  202.          * deltaSearchNonEdgeObjects.
  203.          */
  204.         public int deltasFound;

  205.         /** The total count of objects in the pack. */
  206.         public long totalObjects;

  207.         /**
  208.          * The count of objects that needed to be discovered through an object
  209.          * walk because they were not found in bitmap indices.
  210.          */
  211.         public long bitmapIndexMisses;

  212.         /** The total count of deltas output. */
  213.         public long totalDeltas;

  214.         /** The count of reused objects in the pack. */
  215.         public long reusedObjects;

  216.         /** The count of reused deltas in the pack. */
  217.         public long reusedDeltas;

  218.         /** The count of total bytes in the pack. */
  219.         public long totalBytes;

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

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

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

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

  228.         /** Time in ms spent compressing the pack. */
  229.         public long timeCompressing;

  230.         /** Time in ms spent writing the pack. */
  231.         public long timeWriting;

  232.         /** Time in ms spent checking reachability.
  233.          *
  234.          * @since 5.10
  235.          */
  236.         public long reachabilityCheckDuration;

  237.         /** Number of trees traversed in the walk when writing the pack.
  238.          *
  239.          * @since 5.4
  240.          */
  241.         public long treesTraversed;

  242.         /**
  243.          * Amount of packfile uris sent to the client to download via HTTP.
  244.          *
  245.          * @since 5.6
  246.          */
  247.         public long offloadedPackfiles;

  248.         /**
  249.          * Total size (in bytes) offloaded to HTTP downloads.
  250.          *
  251.          * @since 5.6
  252.          */
  253.         public long offloadedPackfileSize;

  254.         /**
  255.          * Statistics about each object type in the pack (commits, tags, trees
  256.          * and blobs.)
  257.          */
  258.         public ObjectType.Accumulator[] objectTypes;

  259.         {
  260.             objectTypes = new ObjectType.Accumulator[5];
  261.             objectTypes[OBJ_COMMIT] = new ObjectType.Accumulator();
  262.             objectTypes[OBJ_TREE] = new ObjectType.Accumulator();
  263.             objectTypes[OBJ_BLOB] = new ObjectType.Accumulator();
  264.             objectTypes[OBJ_TAG] = new ObjectType.Accumulator();
  265.         }
  266.     }

  267.     private Accumulator statistics;

  268.     /**
  269.      * Creates a new {@link org.eclipse.jgit.storage.pack.PackStatistics} object
  270.      * from the accumulator.
  271.      *
  272.      * @param accumulator
  273.      *            the accumulator of the statistics
  274.      */
  275.     public PackStatistics(Accumulator accumulator) {
  276.         /*
  277.          * For efficiency this wraps and serves up the Accumulator object rather
  278.          * than making a deep clone. Normal usage of PackWriter is to create a
  279.          * single pack/index/bitmap and only call getStatistics() after all work
  280.          * is complete.
  281.          */
  282.         statistics = accumulator;
  283.     }

  284.     /**
  285.      * Get the count of references in the ref advertisement.
  286.      *
  287.      * @return count of refs in the ref advertisement.
  288.      * @since 4.11
  289.      */
  290.     public long getAdvertised() {
  291.         return statistics.advertised;
  292.     }

  293.     /**
  294.      * Get the count of client wants.
  295.      *
  296.      * @return count of client wants.
  297.      * @since 4.11
  298.      */
  299.     public long getWants() {
  300.         return statistics.wants;
  301.     }

  302.     /**
  303.      * Get the count of client haves.
  304.      *
  305.      * @return count of client haves.
  306.      * @since 4.11
  307.      */
  308.     public long getHaves() {
  309.         return statistics.haves;
  310.     }

  311.     /**
  312.      * Get the count of client wants that were not advertised by the server.
  313.      *
  314.      * @return count of client wants that were not advertised by the server.
  315.      * @since 5.10
  316.      */
  317.     public long getNotAdvertisedWants() {
  318.         return statistics.notAdvertisedWants;
  319.     }

  320.     /**
  321.      * Time in ms spent in the negotiation phase. For non-bidirectional
  322.      * transports (e.g., HTTP), this is only for the final request that sends
  323.      * back the pack file.
  324.      *
  325.      * @return time for ref advertisement in ms.
  326.      * @since 4.11
  327.      */
  328.     public long getTimeNegotiating() {
  329.         return statistics.timeNegotiating;
  330.     }

  331.     /**
  332.      * Get unmodifiable collection of objects to be included in the pack.
  333.      *
  334.      * @return unmodifiable collection of objects to be included in the pack.
  335.      *         May be {@code null} if the pack was hand-crafted in a unit test.
  336.      */
  337.     public Set<ObjectId> getInterestingObjects() {
  338.         return statistics.interestingObjects;
  339.     }

  340.     /**
  341.      * Get unmodifiable collection of objects that should be excluded from the
  342.      * pack
  343.      *
  344.      * @return unmodifiable collection of objects that should be excluded from
  345.      *         the pack, as the peer that will receive the pack already has
  346.      *         these objects.
  347.      */
  348.     public Set<ObjectId> getUninterestingObjects() {
  349.         return statistics.uninterestingObjects;
  350.     }

  351.     /**
  352.      * Get unmodifiable collection of objects that were shallow commits on the
  353.      * client.
  354.      *
  355.      * @return unmodifiable collection of objects that were shallow commits on
  356.      *         the client.
  357.      */
  358.     public Set<ObjectId> getClientShallowCommits() {
  359.         return statistics.clientShallowCommits;
  360.     }

  361.     /**
  362.      * Get unmodifiable list of the cached packs that were reused in the output
  363.      *
  364.      * @return unmodifiable list of the cached packs that were reused in the
  365.      *         output, if any were selected for reuse.
  366.      */
  367.     public List<CachedPack> getReusedPacks() {
  368.         return statistics.reusedPacks;
  369.     }

  370.     /**
  371.      * Get unmodifiable collection of the root commits of the history.
  372.      *
  373.      * @return unmodifiable collection of the root commits of the history.
  374.      */
  375.     public Set<ObjectId> getRootCommits() {
  376.         return statistics.rootCommits;
  377.     }

  378.     /**
  379.      * Get number of objects in the output pack that went through the delta
  380.      * search process in order to find a potential delta base.
  381.      *
  382.      * @return number of objects in the output pack that went through the delta
  383.      *         search process in order to find a potential delta base.
  384.      */
  385.     public int getDeltaSearchNonEdgeObjects() {
  386.         return statistics.deltaSearchNonEdgeObjects;
  387.     }

  388.     /**
  389.      * Get number of objects in the output pack that went through delta base
  390.      * search and found a suitable base.
  391.      *
  392.      * @return number of objects in the output pack that went through delta base
  393.      *         search and found a suitable base. This is a subset of
  394.      *         {@link #getDeltaSearchNonEdgeObjects()}.
  395.      */
  396.     public int getDeltasFound() {
  397.         return statistics.deltasFound;
  398.     }

  399.     /**
  400.      * Get total number of objects output.
  401.      *
  402.      * @return total number of objects output. This total includes the value of
  403.      *         {@link #getTotalDeltas()}.
  404.      */
  405.     public long getTotalObjects() {
  406.         return statistics.totalObjects;
  407.     }

  408.     /**
  409.      * Get the count of objects that needed to be discovered through an object
  410.      * walk because they were not found in bitmap indices.
  411.      *
  412.      * @return the count of objects that needed to be discovered through an
  413.      *         object walk because they were not found in bitmap indices.
  414.      *         Returns -1 if no bitmap indices were found.
  415.      */
  416.     public long getBitmapIndexMisses() {
  417.         return statistics.bitmapIndexMisses;
  418.     }

  419.     /**
  420.      * Get total number of deltas output.
  421.      *
  422.      * @return total number of deltas output. This may be lower than the actual
  423.      *         number of deltas if a cached pack was reused.
  424.      */
  425.     public long getTotalDeltas() {
  426.         return statistics.totalDeltas;
  427.     }

  428.     /**
  429.      * Get number of objects whose existing representation was reused in the
  430.      * output.
  431.      *
  432.      * @return number of objects whose existing representation was reused in the
  433.      *         output. This count includes {@link #getReusedDeltas()}.
  434.      */
  435.     public long getReusedObjects() {
  436.         return statistics.reusedObjects;
  437.     }

  438.     /**
  439.      * Get number of deltas whose existing representation was reused in the
  440.      * output.
  441.      *
  442.      * @return number of deltas whose existing representation was reused in the
  443.      *         output, as their base object was also output or was assumed
  444.      *         present for a thin pack. This may be lower than the actual number
  445.      *         of reused deltas if a cached pack was reused.
  446.      */
  447.     public long getReusedDeltas() {
  448.         return statistics.reusedDeltas;
  449.     }

  450.     /**
  451.      * Get total number of bytes written.
  452.      *
  453.      * @return total number of bytes written. This size includes the pack
  454.      *         header, trailer, thin pack, and reused cached pack(s).
  455.      */
  456.     public long getTotalBytes() {
  457.         return statistics.totalBytes;
  458.     }

  459.     /**
  460.      * Get size of the thin pack in bytes.
  461.      *
  462.      * @return size of the thin pack in bytes, if a thin pack was generated. A
  463.      *         thin pack is created when the client already has objects and some
  464.      *         deltas are created against those objects, or if a cached pack is
  465.      *         being used and some deltas will reference objects in the cached
  466.      *         pack. This size does not include the pack header or trailer.
  467.      */
  468.     public long getThinPackBytes() {
  469.         return statistics.thinPackBytes;
  470.     }

  471.     /**
  472.      * Get information about this type of object in the pack.
  473.      *
  474.      * @param typeCode
  475.      *            object type code, e.g. OBJ_COMMIT or OBJ_TREE.
  476.      * @return information about this type of object in the pack.
  477.      */
  478.     public ObjectType byObjectType(int typeCode) {
  479.         return new ObjectType(statistics.objectTypes[typeCode]);
  480.     }

  481.     /**
  482.      * Whether the resulting pack file was a shallow pack.
  483.      *
  484.      * @return {@code true} if the resulting pack file was a shallow pack.
  485.      */
  486.     public boolean isShallow() {
  487.         return statistics.depth > 0;
  488.     }

  489.     /**
  490.      * Get depth (in commits) the pack includes if shallow.
  491.      *
  492.      * @return depth (in commits) the pack includes if shallow.
  493.      */
  494.     public int getDepth() {
  495.         return statistics.depth;
  496.     }

  497.     /**
  498.      * Get time in milliseconds spent enumerating the objects that need to be
  499.      * included in the output.
  500.      *
  501.      * @return time in milliseconds spent enumerating the objects that need to
  502.      *         be included in the output. This time includes any restarts that
  503.      *         occur when a cached pack is selected for reuse.
  504.      */
  505.     public long getTimeCounting() {
  506.         return statistics.timeCounting;
  507.     }

  508.     /**
  509.      * Get time in milliseconds spent matching existing representations against
  510.      * objects that will be transmitted.
  511.      *
  512.      * @return time in milliseconds spent matching existing representations
  513.      *         against objects that will be transmitted, or that the client can
  514.      *         be assumed to already have.
  515.      */
  516.     public long getTimeSearchingForReuse() {
  517.         return statistics.timeSearchingForReuse;
  518.     }

  519.     /**
  520.      * Get time in milliseconds spent finding the sizes of all objects that will
  521.      * enter the delta compression search window.
  522.      *
  523.      * @return time in milliseconds spent finding the sizes of all objects that
  524.      *         will enter the delta compression search window. The sizes need to
  525.      *         be known to better match similar objects together and improve
  526.      *         delta compression ratios.
  527.      */
  528.     public long getTimeSearchingForSizes() {
  529.         return statistics.timeSearchingForSizes;
  530.     }

  531.     /**
  532.      * Get time in milliseconds spent on delta compression.
  533.      *
  534.      * @return time in milliseconds spent on delta compression. This is observed
  535.      *         wall-clock time and does not accurately track CPU time used when
  536.      *         multiple threads were used to perform the delta compression.
  537.      */
  538.     public long getTimeCompressing() {
  539.         return statistics.timeCompressing;
  540.     }

  541.     /**
  542.      * Get time in milliseconds spent writing the pack output, from start of
  543.      * header until end of trailer.
  544.      *
  545.      * @return time in milliseconds spent writing the pack output, from start of
  546.      *         header until end of trailer. The transfer speed can be
  547.      *         approximated by dividing {@link #getTotalBytes()} by this value.
  548.      */
  549.     public long getTimeWriting() {
  550.         return statistics.timeWriting;
  551.     }

  552.     /**
  553.      * Get time in milliseconds spent checking if the client has access to the
  554.      * commits they are requesting.
  555.      *
  556.      * @return time in milliseconds spent checking if the client has access to the
  557.      * commits they are requesting.
  558.      * @since 5.10
  559.      */
  560.     public long getReachabilityCheckDuration() {
  561.         return statistics.reachabilityCheckDuration;
  562.     }

  563.     /**
  564.      * @return number of trees traversed in the walk when writing the pack.
  565.      * @since 5.4
  566.      */
  567.     public long getTreesTraversed() {
  568.         return statistics.treesTraversed;
  569.     }

  570.     /**
  571.      * @return amount of packfiles offloaded (sent as "packfile-uri")/
  572.      * @since 5.6
  573.      */
  574.     public long getOffloadedPackfiles() {
  575.         return statistics.offloadedPackfiles;
  576.     }

  577.     /**
  578.      * @return total size (in bytes) offloaded to HTTP downloads.
  579.      * @since 5.6
  580.      */
  581.     public long getOffloadedPackfilesSize() {
  582.         return statistics.offloadedPackfileSize;
  583.     }

  584.     /**
  585.      * Get total time spent processing this pack.
  586.      *
  587.      * @return total time spent processing this pack.
  588.      */
  589.     public long getTimeTotal() {
  590.         return statistics.timeCounting + statistics.timeSearchingForReuse
  591.                 + statistics.timeSearchingForSizes + statistics.timeCompressing
  592.                 + statistics.timeWriting;
  593.     }

  594.     /**
  595.      * Get the average output speed in terms of bytes-per-second.
  596.      *
  597.      * @return the average output speed in terms of bytes-per-second.
  598.      *         {@code getTotalBytes() / (getTimeWriting() / 1000.0)}.
  599.      */
  600.     public double getTransferRate() {
  601.         return getTotalBytes() / (getTimeWriting() / 1000.0);
  602.     }

  603.     /**
  604.      * Get formatted message string for display to clients.
  605.      *
  606.      * @return formatted message string for display to clients.
  607.      */
  608.     public String getMessage() {
  609.         return MessageFormat.format(JGitText.get().packWriterStatistics,
  610.                 Long.valueOf(statistics.totalObjects),
  611.                 Long.valueOf(statistics.totalDeltas),
  612.                 Long.valueOf(statistics.reusedObjects),
  613.                 Long.valueOf(statistics.reusedDeltas));
  614.     }

  615.     /**
  616.      * Get a map containing ObjectType statistics.
  617.      *
  618.      * @return a map containing ObjectType statistics.
  619.      */
  620.     public Map<Integer, ObjectType> getObjectTypes() {
  621.         HashMap<Integer, ObjectType> map = new HashMap<>();
  622.         map.put(Integer.valueOf(OBJ_BLOB), new ObjectType(
  623.                 statistics.objectTypes[OBJ_BLOB]));
  624.         map.put(Integer.valueOf(OBJ_COMMIT), new ObjectType(
  625.                 statistics.objectTypes[OBJ_COMMIT]));
  626.         map.put(Integer.valueOf(OBJ_TAG), new ObjectType(
  627.                 statistics.objectTypes[OBJ_TAG]));
  628.         map.put(Integer.valueOf(OBJ_TREE), new ObjectType(
  629.                 statistics.objectTypes[OBJ_TREE]));
  630.         return map;
  631.     }
  632. }