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