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 		/**
170 		 * The count of references in the ref advertisement.
171 		 *
172 		 * @since 4.11
173 		 */
174 		public long advertised;
175 
176 		/**
177 		 * The count of client wants.
178 		 *
179 		 * @since 4.11
180 		 */
181 		public long wants;
182 
183 		/**
184 		 * The count of client haves.
185 		 *
186 		 * @since 4.11
187 		 */
188 		public long haves;
189 
190 		/**
191 		 * Time in ms spent in the negotiation phase. For non-bidirectional
192 		 * transports (e.g., HTTP), this is only for the final request that
193 		 * sends back the pack file.
194 		 *
195 		 * @since 4.11
196 		 */
197 		public long timeNegotiating;
198 
199 		/** The set of objects to be included in the pack. */
200 		public Set<ObjectId> interestingObjects;
201 
202 		/** The set of objects to be excluded from the pack. */
203 		public Set<ObjectId> uninterestingObjects;
204 
205 		/** The set of shallow commits on the client. */
206 		public Set<ObjectId> clientShallowCommits;
207 
208 		/** The collection of reused packs in the upload. */
209 		public List<CachedPack> reusedPacks;
210 
211 		/** Commits with no parents. */
212 		public Set<ObjectId> rootCommits;
213 
214 		/** If a shallow pack, the depth in commits. */
215 		public int depth;
216 
217 		/**
218 		 * The count of objects in the pack that went through the delta search
219 		 * process in order to find a potential delta base.
220 		 */
221 		public int deltaSearchNonEdgeObjects;
222 
223 		/**
224 		 * The count of objects in the pack that went through delta base search
225 		 * and found a suitable base. This is a subset of
226 		 * deltaSearchNonEdgeObjects.
227 		 */
228 		public int deltasFound;
229 
230 		/** The total count of objects in the pack. */
231 		public long totalObjects;
232 
233 		/**
234 		 * The count of objects that needed to be discovered through an object
235 		 * walk because they were not found in bitmap indices.
236 		 */
237 		public long bitmapIndexMisses;
238 
239 		/** The total count of deltas output. */
240 		public long totalDeltas;
241 
242 		/** The count of reused objects in the pack. */
243 		public long reusedObjects;
244 
245 		/** The count of reused deltas in the pack. */
246 		public long reusedDeltas;
247 
248 		/** The count of total bytes in the pack. */
249 		public long totalBytes;
250 
251 		/** The size of the thin pack in bytes, if a thin pack was generated. */
252 		public long thinPackBytes;
253 
254 		/** Time in ms spent counting the objects that will go into the pack. */
255 		public long timeCounting;
256 
257 		/** Time in ms spent searching for objects to reuse. */
258 		public long timeSearchingForReuse;
259 
260 		/** Time in ms spent searching for sizes of objects. */
261 		public long timeSearchingForSizes;
262 
263 		/** Time in ms spent compressing the pack. */
264 		public long timeCompressing;
265 
266 		/** Time in ms spent writing the pack. */
267 		public long timeWriting;
268 
269 		/** Number of trees traversed in the walk when writing the pack.
270 		 * @since 5.4*/
271 		public long treesTraversed;
272 
273 		/**
274 		 * Statistics about each object type in the pack (commits, tags, trees
275 		 * and blobs.)
276 		 */
277 		public ObjectType.Accumulator[] objectTypes;
278 
279 		{
280 			objectTypes = new ObjectType.Accumulator[5];
281 			objectTypes[OBJ_COMMIT] = new ObjectType.Accumulator();
282 			objectTypes[OBJ_TREE] = new ObjectType.Accumulator();
283 			objectTypes[OBJ_BLOB] = new ObjectType.Accumulator();
284 			objectTypes[OBJ_TAG] = new ObjectType.Accumulator();
285 		}
286 	}
287 
288 	private Accumulator statistics;
289 
290 	/**
291 	 * Creates a new {@link org.eclipse.jgit.storage.pack.PackStatistics} object
292 	 * from the accumulator.
293 	 *
294 	 * @param accumulator
295 	 *            the accumulator of the statistics
296 	 */
297 	public PackStatistics(Accumulator accumulator) {
298 		/*
299 		 * For efficiency this wraps and serves up the Accumulator object rather
300 		 * than making a deep clone. Normal usage of PackWriter is to create a
301 		 * single pack/index/bitmap and only call getStatistics() after all work
302 		 * is complete.
303 		 */
304 		statistics = accumulator;
305 	}
306 
307 	/**
308 	 * Get the count of references in the ref advertisement.
309 	 *
310 	 * @return count of refs in the ref advertisement.
311 	 * @since 4.11
312 	 */
313 	public long getAdvertised() {
314 		return statistics.advertised;
315 	}
316 
317 	/**
318 	 * Get the count of client wants.
319 	 *
320 	 * @return count of client wants.
321 	 * @since 4.11
322 	 */
323 	public long getWants() {
324 		return statistics.wants;
325 	}
326 
327 	/**
328 	 * Get the count of client haves.
329 	 *
330 	 * @return count of client haves.
331 	 * @since 4.11
332 	 */
333 	public long getHaves() {
334 		return statistics.haves;
335 	}
336 
337 	/**
338 	 * Time in ms spent in the negotiation phase. For non-bidirectional
339 	 * transports (e.g., HTTP), this is only for the final request that sends
340 	 * back the pack file.
341 	 *
342 	 * @return time for ref advertisement in ms.
343 	 * @since 4.11
344 	 */
345 	public long getTimeNegotiating() {
346 		return statistics.timeNegotiating;
347 	}
348 
349 	/**
350 	 * Get unmodifiable collection of objects to be included in the pack.
351 	 *
352 	 * @return unmodifiable collection of objects to be included in the pack.
353 	 *         May be {@code null} if the pack was hand-crafted in a unit test.
354 	 */
355 	public Set<ObjectId> getInterestingObjects() {
356 		return statistics.interestingObjects;
357 	}
358 
359 	/**
360 	 * Get unmodifiable collection of objects that should be excluded from the
361 	 * pack
362 	 *
363 	 * @return unmodifiable collection of objects that should be excluded from
364 	 *         the pack, as the peer that will receive the pack already has
365 	 *         these objects.
366 	 */
367 	public Set<ObjectId> getUninterestingObjects() {
368 		return statistics.uninterestingObjects;
369 	}
370 
371 	/**
372 	 * Get unmodifiable collection of objects that were shallow commits on the
373 	 * client.
374 	 *
375 	 * @return unmodifiable collection of objects that were shallow commits on
376 	 *         the client.
377 	 */
378 	public Set<ObjectId> getClientShallowCommits() {
379 		return statistics.clientShallowCommits;
380 	}
381 
382 	/**
383 	 * Get unmodifiable list of the cached packs that were reused in the output
384 	 *
385 	 * @return unmodifiable list of the cached packs that were reused in the
386 	 *         output, if any were selected for reuse.
387 	 */
388 	public List<CachedPack> getReusedPacks() {
389 		return statistics.reusedPacks;
390 	}
391 
392 	/**
393 	 * Get unmodifiable collection of the root commits of the history.
394 	 *
395 	 * @return unmodifiable collection of the root commits of the history.
396 	 */
397 	public Set<ObjectId> getRootCommits() {
398 		return statistics.rootCommits;
399 	}
400 
401 	/**
402 	 * Get number of objects in the output pack that went through the delta
403 	 * search process in order to find a potential delta base.
404 	 *
405 	 * @return number of objects in the output pack that went through the delta
406 	 *         search process in order to find a potential delta base.
407 	 */
408 	public int getDeltaSearchNonEdgeObjects() {
409 		return statistics.deltaSearchNonEdgeObjects;
410 	}
411 
412 	/**
413 	 * Get number of objects in the output pack that went through delta base
414 	 * search and found a suitable base.
415 	 *
416 	 * @return number of objects in the output pack that went through delta base
417 	 *         search and found a suitable base. This is a subset of
418 	 *         {@link #getDeltaSearchNonEdgeObjects()}.
419 	 */
420 	public int getDeltasFound() {
421 		return statistics.deltasFound;
422 	}
423 
424 	/**
425 	 * Get total number of objects output.
426 	 *
427 	 * @return total number of objects output. This total includes the value of
428 	 *         {@link #getTotalDeltas()}.
429 	 */
430 	public long getTotalObjects() {
431 		return statistics.totalObjects;
432 	}
433 
434 	/**
435 	 * Get the count of objects that needed to be discovered through an object
436 	 * walk because they were not found in bitmap indices.
437 	 *
438 	 * @return the count of objects that needed to be discovered through an
439 	 *         object walk because they were not found in bitmap indices.
440 	 *         Returns -1 if no bitmap indices were found.
441 	 */
442 	public long getBitmapIndexMisses() {
443 		return statistics.bitmapIndexMisses;
444 	}
445 
446 	/**
447 	 * Get total number of deltas output.
448 	 *
449 	 * @return total number of deltas output. This may be lower than the actual
450 	 *         number of deltas if a cached pack was reused.
451 	 */
452 	public long getTotalDeltas() {
453 		return statistics.totalDeltas;
454 	}
455 
456 	/**
457 	 * Get number of objects whose existing representation was reused in the
458 	 * output.
459 	 *
460 	 * @return number of objects whose existing representation was reused in the
461 	 *         output. This count includes {@link #getReusedDeltas()}.
462 	 */
463 	public long getReusedObjects() {
464 		return statistics.reusedObjects;
465 	}
466 
467 	/**
468 	 * Get number of deltas whose existing representation was reused in the
469 	 * output.
470 	 *
471 	 * @return number of deltas whose existing representation was reused in the
472 	 *         output, as their base object was also output or was assumed
473 	 *         present for a thin pack. This may be lower than the actual number
474 	 *         of reused deltas if a cached pack was reused.
475 	 */
476 	public long getReusedDeltas() {
477 		return statistics.reusedDeltas;
478 	}
479 
480 	/**
481 	 * Get total number of bytes written.
482 	 *
483 	 * @return total number of bytes written. This size includes the pack
484 	 *         header, trailer, thin pack, and reused cached pack(s).
485 	 */
486 	public long getTotalBytes() {
487 		return statistics.totalBytes;
488 	}
489 
490 	/**
491 	 * Get size of the thin pack in bytes.
492 	 *
493 	 * @return size of the thin pack in bytes, if a thin pack was generated. A
494 	 *         thin pack is created when the client already has objects and some
495 	 *         deltas are created against those objects, or if a cached pack is
496 	 *         being used and some deltas will reference objects in the cached
497 	 *         pack. This size does not include the pack header or trailer.
498 	 */
499 	public long getThinPackBytes() {
500 		return statistics.thinPackBytes;
501 	}
502 
503 	/**
504 	 * Get information about this type of object in the pack.
505 	 *
506 	 * @param typeCode
507 	 *            object type code, e.g. OBJ_COMMIT or OBJ_TREE.
508 	 * @return information about this type of object in the pack.
509 	 */
510 	public ObjectType byObjectType(int typeCode) {
511 		return new ObjectType(statistics.objectTypes[typeCode]);
512 	}
513 
514 	/**
515 	 * Whether the resulting pack file was a shallow pack.
516 	 *
517 	 * @return {@code true} if the resulting pack file was a shallow pack.
518 	 */
519 	public boolean isShallow() {
520 		return statistics.depth > 0;
521 	}
522 
523 	/**
524 	 * Get depth (in commits) the pack includes if shallow.
525 	 *
526 	 * @return depth (in commits) the pack includes if shallow.
527 	 */
528 	public int getDepth() {
529 		return statistics.depth;
530 	}
531 
532 	/**
533 	 * Get time in milliseconds spent enumerating the objects that need to be
534 	 * included in the output.
535 	 *
536 	 * @return time in milliseconds spent enumerating the objects that need to
537 	 *         be included in the output. This time includes any restarts that
538 	 *         occur when a cached pack is selected for reuse.
539 	 */
540 	public long getTimeCounting() {
541 		return statistics.timeCounting;
542 	}
543 
544 	/**
545 	 * Get time in milliseconds spent matching existing representations against
546 	 * objects that will be transmitted.
547 	 *
548 	 * @return time in milliseconds spent matching existing representations
549 	 *         against objects that will be transmitted, or that the client can
550 	 *         be assumed to already have.
551 	 */
552 	public long getTimeSearchingForReuse() {
553 		return statistics.timeSearchingForReuse;
554 	}
555 
556 	/**
557 	 * Get time in milliseconds spent finding the sizes of all objects that will
558 	 * enter the delta compression search window.
559 	 *
560 	 * @return time in milliseconds spent finding the sizes of all objects that
561 	 *         will enter the delta compression search window. The sizes need to
562 	 *         be known to better match similar objects together and improve
563 	 *         delta compression ratios.
564 	 */
565 	public long getTimeSearchingForSizes() {
566 		return statistics.timeSearchingForSizes;
567 	}
568 
569 	/**
570 	 * Get time in milliseconds spent on delta compression.
571 	 *
572 	 * @return time in milliseconds spent on delta compression. This is observed
573 	 *         wall-clock time and does not accurately track CPU time used when
574 	 *         multiple threads were used to perform the delta compression.
575 	 */
576 	public long getTimeCompressing() {
577 		return statistics.timeCompressing;
578 	}
579 
580 	/**
581 	 * Get time in milliseconds spent writing the pack output, from start of
582 	 * header until end of trailer.
583 	 *
584 	 * @return time in milliseconds spent writing the pack output, from start of
585 	 *         header until end of trailer. The transfer speed can be
586 	 *         approximated by dividing {@link #getTotalBytes()} by this value.
587 	 */
588 	public long getTimeWriting() {
589 		return statistics.timeWriting;
590 	}
591 
592 	/**
593 	 * @return number of trees traversed in the walk when writing the pack.
594 	 * @since 5.4
595 	 */
596 	public long getTreesTraversed() {
597 		return statistics.treesTraversed;
598 	}
599 
600 	/**
601 	 * Get total time spent processing this pack.
602 	 *
603 	 * @return total time spent processing this pack.
604 	 */
605 	public long getTimeTotal() {
606 		return statistics.timeCounting + statistics.timeSearchingForReuse
607 				+ statistics.timeSearchingForSizes + statistics.timeCompressing
608 				+ statistics.timeWriting;
609 	}
610 
611 	/**
612 	 * Get the average output speed in terms of bytes-per-second.
613 	 *
614 	 * @return the average output speed in terms of bytes-per-second.
615 	 *         {@code getTotalBytes() / (getTimeWriting() / 1000.0)}.
616 	 */
617 	public double getTransferRate() {
618 		return getTotalBytes() / (getTimeWriting() / 1000.0);
619 	}
620 
621 	/**
622 	 * Get formatted message string for display to clients.
623 	 *
624 	 * @return formatted message string for display to clients.
625 	 */
626 	public String getMessage() {
627 		return MessageFormat.format(JGitText.get().packWriterStatistics,
628 				Long.valueOf(statistics.totalObjects),
629 				Long.valueOf(statistics.totalDeltas),
630 				Long.valueOf(statistics.reusedObjects),
631 				Long.valueOf(statistics.reusedDeltas));
632 	}
633 
634 	/**
635 	 * Get a map containing ObjectType statistics.
636 	 *
637 	 * @return a map containing ObjectType statistics.
638 	 */
639 	public Map<Integer, ObjectType> getObjectTypes() {
640 		HashMap<Integer, ObjectType> map = new HashMap<>();
641 		map.put(Integer.valueOf(OBJ_BLOB), new ObjectType(
642 				statistics.objectTypes[OBJ_BLOB]));
643 		map.put(Integer.valueOf(OBJ_COMMIT), new ObjectType(
644 				statistics.objectTypes[OBJ_COMMIT]));
645 		map.put(Integer.valueOf(OBJ_TAG), new ObjectType(
646 				statistics.objectTypes[OBJ_TAG]));
647 		map.put(Integer.valueOf(OBJ_TREE), new ObjectType(
648 				statistics.objectTypes[OBJ_TREE]));
649 		return map;
650 	}
651 }