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 }