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