View Javadoc
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  
43  package org.eclipse.jgit.storage.pack;
44  
45  import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
46  import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
47  import static org.eclipse.jgit.lib.Constants.OBJ_TAG;
48  import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
49  
50  import java.text.MessageFormat;
51  import java.util.HashMap;
52  import java.util.List;
53  import java.util.Map;
54  import java.util.Set;
55  
56  import org.eclipse.jgit.internal.JGitText;
57  import org.eclipse.jgit.internal.storage.pack.CachedPack;
58  import org.eclipse.jgit.lib.ObjectId;
59  
60  /**
61   * Statistics about {@link org.eclipse.jgit.internal.storage.pack.PackWriter}
62   * pack creation.
63   *
64   * @since 4.1
65   */
66  public class PackStatistics {
67  	/**
68  	 * Statistics about a single type of object (commits, tags, trees and
69  	 * blobs).
70  	 */
71  	public static class ObjectType {
72  		/**
73  		 * POJO for accumulating the ObjectType statistics.
74  		 */
75  		public static class Accumulator {
76  			/** Count of objects of this type. */
77  			public long cntObjects;
78  
79  			/** Count of deltas of this type. */
80  			public long cntDeltas;
81  
82  			/** Count of reused objects of this type. */
83  			public long reusedObjects;
84  
85  			/** Count of reused deltas of this type. */
86  			public long reusedDeltas;
87  
88  			/** Count of bytes for all objects of this type. */
89  			public long bytes;
90  
91  			/** Count of delta bytes for objects of this type. */
92  			public long deltaBytes;
93  		}
94  
95  		private ObjectType.Accumulator objectType;
96  
97  		/**
98  		 * Creates a new {@link ObjectType} object from the accumulator.
99  		 *
100 		 * @param accumulator
101 		 *            the accumulator of the statistics
102 		 */
103 		public ObjectType(ObjectType.Accumulator accumulator) {
104 			/*
105 			 * For efficiency this wraps and serves up the Accumulator object
106 			 * rather than making a deep clone. Normal usage of PackWriter is to
107 			 * create a single pack/index/bitmap and only call getStatistics()
108 			 * after all work is complete.
109 			 */
110 			objectType = accumulator;
111 		}
112 
113 		/**
114 		 * @return total number of objects output. This total includes the value
115 		 *         of {@link #getDeltas()}.
116 		 */
117 		public long getObjects() {
118 			return objectType.cntObjects;
119 		}
120 
121 		/**
122 		 * @return total number of deltas output. This may be lower than the
123 		 *         actual number of deltas if a cached pack was reused.
124 		 */
125 		public long getDeltas() {
126 			return objectType.cntDeltas;
127 		}
128 
129 		/**
130 		 * @return number of objects whose existing representation was reused in
131 		 *         the output. This count includes {@link #getReusedDeltas()}.
132 		 */
133 		public long getReusedObjects() {
134 			return objectType.reusedObjects;
135 		}
136 
137 		/**
138 		 * @return number of deltas whose existing representation was reused in
139 		 *         the output, as their base object was also output or was
140 		 *         assumed present for a thin pack. This may be lower than the
141 		 *         actual number of reused deltas if a cached pack was reused.
142 		 */
143 		public long getReusedDeltas() {
144 			return objectType.reusedDeltas;
145 		}
146 
147 		/**
148 		 * @return total number of bytes written. This size includes the object
149 		 *         headers as well as the compressed data. This size also
150 		 *         includes all of {@link #getDeltaBytes()}.
151 		 */
152 		public long getBytes() {
153 			return objectType.bytes;
154 		}
155 
156 		/**
157 		 * @return number of delta bytes written. This size includes the object
158 		 *         headers for the delta objects.
159 		 */
160 		public long getDeltaBytes() {
161 			return objectType.deltaBytes;
162 		}
163 	}
164 
165 	/**
166 	 * POJO for accumulating the statistics.
167 	 */
168 	public static class Accumulator {
169 		/** The set of objects to be included in the pack. */
170 		public Set<ObjectId> interestingObjects;
171 
172 		/** The set of objects to be excluded from the pack. */
173 		public Set<ObjectId> uninterestingObjects;
174 
175 		/** The set of shallow commits on the client. */
176 		public Set<ObjectId> clientShallowCommits;
177 
178 		/** The collection of reused packs in the upload. */
179 		public List<CachedPack> reusedPacks;
180 
181 		/** Commits with no parents. */
182 		public Set<ObjectId> rootCommits;
183 
184 		/** If a shallow pack, the depth in commits. */
185 		public int depth;
186 
187 		/**
188 		 * The count of objects in the pack that went through the delta search
189 		 * process in order to find a potential delta base.
190 		 */
191 		public int deltaSearchNonEdgeObjects;
192 
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 
200 		/** The total count of objects in the pack. */
201 		public long totalObjects;
202 
203 		/**
204 		 * The count of objects that needed to be discovered through an object
205 		 * walk because they were not found in bitmap indices.
206 		 */
207 		public long bitmapIndexMisses;
208 
209 		/** The total count of deltas output. */
210 		public long totalDeltas;
211 
212 		/** The count of reused objects in the pack. */
213 		public long reusedObjects;
214 
215 		/** The count of reused deltas in the pack. */
216 		public long reusedDeltas;
217 
218 		/** The count of total bytes in the pack. */
219 		public long totalBytes;
220 
221 		/** The size of the thin pack in bytes, if a thin pack was generated. */
222 		public long thinPackBytes;
223 
224 		/** Time in ms spent counting the objects that will go into the pack. */
225 		public long timeCounting;
226 
227 		/** Time in ms spent searching for objects to reuse. */
228 		public long timeSearchingForReuse;
229 
230 		/** Time in ms spent searching for sizes of objects. */
231 		public long timeSearchingForSizes;
232 
233 		/** Time in ms spent compressing the pack. */
234 		public long timeCompressing;
235 
236 		/** Time in ms spent writing the pack. */
237 		public long timeWriting;
238 
239 		/**
240 		 * Statistics about each object type in the pack (commits, tags, trees
241 		 * and blobs.)
242 		 */
243 		public ObjectType.Accumulator[] objectTypes;
244 
245 		{
246 			objectTypes = new ObjectType.Accumulator[5];
247 			objectTypes[OBJ_COMMIT] = new ObjectType.Accumulator();
248 			objectTypes[OBJ_TREE] = new ObjectType.Accumulator();
249 			objectTypes[OBJ_BLOB] = new ObjectType.Accumulator();
250 			objectTypes[OBJ_TAG] = new ObjectType.Accumulator();
251 		}
252 	}
253 
254 	private Accumulator statistics;
255 
256 	/**
257 	 * Creates a new {@link PackStatistics} object from the accumulator.
258 	 *
259 	 * @param accumulator
260 	 *            the accumulator of the statistics
261 	 */
262 	public PackStatistics(Accumulator accumulator) {
263 		/*
264 		 * For efficiency this wraps and serves up the Accumulator object rather
265 		 * than making a deep clone. Normal usage of PackWriter is to create a
266 		 * single pack/index/bitmap and only call getStatistics() after all work
267 		 * is complete.
268 		 */
269 		statistics = accumulator;
270 	}
271 
272 	/**
273 	 * @return unmodifiable collection of objects to be included in the pack.
274 	 *         May be {@code null} if the pack was hand-crafted in a unit test.
275 	 */
276 	public Set<ObjectId> getInterestingObjects() {
277 		return statistics.interestingObjects;
278 	}
279 
280 	/**
281 	 * @return unmodifiable collection of objects that should be excluded from
282 	 *         the pack, as the peer that will receive the pack already has
283 	 *         these objects.
284 	 */
285 	public Set<ObjectId> getUninterestingObjects() {
286 		return statistics.uninterestingObjects;
287 	}
288 
289 	/**
290 	 * @return unmodifiable collection of objects that were shallow commits on
291 	 *         the client.
292 	 */
293 	public Set<ObjectId> getClientShallowCommits() {
294 		return statistics.clientShallowCommits;
295 	}
296 
297 	/**
298 	 * @return unmodifiable list of the cached packs that were reused in the
299 	 *         output, if any were selected for reuse.
300 	 */
301 	public List<CachedPack> getReusedPacks() {
302 		return statistics.reusedPacks;
303 	}
304 
305 	/** @return unmodifiable collection of the root commits of the history. */
306 	public Set<ObjectId> getRootCommits() {
307 		return statistics.rootCommits;
308 	}
309 
310 	/**
311 	 * @return number of objects in the output pack that went through the delta
312 	 *         search process in order to find a potential delta base.
313 	 */
314 	public int getDeltaSearchNonEdgeObjects() {
315 		return statistics.deltaSearchNonEdgeObjects;
316 	}
317 
318 	/**
319 	 * @return number of objects in the output pack that went through delta base
320 	 *         search and found a suitable base. This is a subset of
321 	 *         {@link #getDeltaSearchNonEdgeObjects()}.
322 	 */
323 	public int getDeltasFound() {
324 		return statistics.deltasFound;
325 	}
326 
327 	/**
328 	 * @return total number of objects output. This total includes the value of
329 	 *         {@link #getTotalDeltas()}.
330 	 */
331 	public long getTotalObjects() {
332 		return statistics.totalObjects;
333 	}
334 
335 	/**
336 	 * @return the count of objects that needed to be discovered through an
337 	 *         object walk because they were not found in bitmap indices.
338 	 *         Returns -1 if no bitmap indices were found.
339 	 */
340 	public long getBitmapIndexMisses() {
341 		return statistics.bitmapIndexMisses;
342 	}
343 
344 	/**
345 	 * @return total number of deltas output. This may be lower than the actual
346 	 *         number of deltas if a cached pack was reused.
347 	 */
348 	public long getTotalDeltas() {
349 		return statistics.totalDeltas;
350 	}
351 
352 	/**
353 	 * @return number of objects whose existing representation was reused in the
354 	 *         output. This count includes {@link #getReusedDeltas()}.
355 	 */
356 	public long getReusedObjects() {
357 		return statistics.reusedObjects;
358 	}
359 
360 	/**
361 	 * @return number of deltas whose existing representation was reused in the
362 	 *         output, as their base object was also output or was assumed
363 	 *         present for a thin pack. This may be lower than the actual number
364 	 *         of reused deltas if a cached pack was reused.
365 	 */
366 	public long getReusedDeltas() {
367 		return statistics.reusedDeltas;
368 	}
369 
370 	/**
371 	 * @return total number of bytes written. This size includes the pack
372 	 *         header, trailer, thin pack, and reused cached pack(s).
373 	 */
374 	public long getTotalBytes() {
375 		return statistics.totalBytes;
376 	}
377 
378 	/**
379 	 * @return size of the thin pack in bytes, if a thin pack was generated. A
380 	 *         thin pack is created when the client already has objects and some
381 	 *         deltas are created against those objects, or if a cached pack is
382 	 *         being used and some deltas will reference objects in the cached
383 	 *         pack. This size does not include the pack header or trailer.
384 	 */
385 	public long getThinPackBytes() {
386 		return statistics.thinPackBytes;
387 	}
388 
389 	/**
390 	 * @param typeCode
391 	 *            object type code, e.g. OBJ_COMMIT or OBJ_TREE.
392 	 * @return information about this type of object in the pack.
393 	 */
394 	public ObjectType byObjectType(int typeCode) {
395 		return new ObjectType(statistics.objectTypes[typeCode]);
396 	}
397 
398 	/** @return true if the resulting pack file was a shallow pack. */
399 	public boolean isShallow() {
400 		return statistics.depth > 0;
401 	}
402 
403 	/** @return depth (in commits) the pack includes if shallow. */
404 	public int getDepth() {
405 		return statistics.depth;
406 	}
407 
408 	/**
409 	 * @return time in milliseconds spent enumerating the objects that need to
410 	 *         be included in the output. This time includes any restarts that
411 	 *         occur when a cached pack is selected for reuse.
412 	 */
413 	public long getTimeCounting() {
414 		return statistics.timeCounting;
415 	}
416 
417 	/**
418 	 * @return time in milliseconds spent matching existing representations
419 	 *         against objects that will be transmitted, or that the client can
420 	 *         be assumed to already have.
421 	 */
422 	public long getTimeSearchingForReuse() {
423 		return statistics.timeSearchingForReuse;
424 	}
425 
426 	/**
427 	 * @return time in milliseconds spent finding the sizes of all objects that
428 	 *         will enter the delta compression search window. The sizes need to
429 	 *         be known to better match similar objects together and improve
430 	 *         delta compression ratios.
431 	 */
432 	public long getTimeSearchingForSizes() {
433 		return statistics.timeSearchingForSizes;
434 	}
435 
436 	/**
437 	 * @return time in milliseconds spent on delta compression. This is observed
438 	 *         wall-clock time and does not accurately track CPU time used when
439 	 *         multiple threads were used to perform the delta compression.
440 	 */
441 	public long getTimeCompressing() {
442 		return statistics.timeCompressing;
443 	}
444 
445 	/**
446 	 * @return time in milliseconds spent writing the pack output, from start of
447 	 *         header until end of trailer. The transfer speed can be
448 	 *         approximated by dividing {@link #getTotalBytes()} by this value.
449 	 */
450 	public long getTimeWriting() {
451 		return statistics.timeWriting;
452 	}
453 
454 	/** @return total time spent processing this pack. */
455 	public long getTimeTotal() {
456 		return statistics.timeCounting + statistics.timeSearchingForReuse
457 				+ statistics.timeSearchingForSizes + statistics.timeCompressing
458 				+ statistics.timeWriting;
459 	}
460 
461 	/**
462 	 * @return get the average output speed in terms of bytes-per-second.
463 	 *         {@code getTotalBytes() / (getTimeWriting() / 1000.0)}.
464 	 */
465 	public double getTransferRate() {
466 		return getTotalBytes() / (getTimeWriting() / 1000.0);
467 	}
468 
469 	/** @return formatted message string for display to clients. */
470 	public String getMessage() {
471 		return MessageFormat.format(JGitText.get().packWriterStatistics,
472 				Long.valueOf(statistics.totalObjects),
473 				Long.valueOf(statistics.totalDeltas),
474 				Long.valueOf(statistics.reusedObjects),
475 				Long.valueOf(statistics.reusedDeltas));
476 	}
477 
478 	/** @return a map containing ObjectType statistics. */
479 	public Map<Integer, ObjectType> getObjectTypes() {
480 		HashMap<Integer, ObjectType> map = new HashMap<>();
481 		map.put(Integer.valueOf(OBJ_BLOB), new ObjectType(
482 				statistics.objectTypes[OBJ_BLOB]));
483 		map.put(Integer.valueOf(OBJ_COMMIT), new ObjectType(
484 				statistics.objectTypes[OBJ_COMMIT]));
485 		map.put(Integer.valueOf(OBJ_TAG), new ObjectType(
486 				statistics.objectTypes[OBJ_TAG]));
487 		map.put(Integer.valueOf(OBJ_TREE), new ObjectType(
488 				statistics.objectTypes[OBJ_TREE]));
489 		return map;
490 	}
491 }