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 }