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 /** The set of objects to be included in the pack. */
170 public Set<ObjectId> interestingObjects;
171
172 /** The set of objects to be excluded from the pack. */
173 public Set<ObjectId> uninterestingObjects;
174
175 /** The set of shallow commits on the client. */
176 public Set<ObjectId> clientShallowCommits;
177
178 /** The collection of reused packs in the upload. */
179 public List<CachedPack> reusedPacks;
180
181 /** Commits with no parents. */
182 public Set<ObjectId> rootCommits;
183
184 /** If a shallow pack, the depth in commits. */
185 public int depth;
186
187 /**
188 * The count of objects in the pack that went through the delta search
189 * process in order to find a potential delta base.
190 */
191 public int deltaSearchNonEdgeObjects;
192
193 /**
194 * The count of objects in the pack that went through delta base search
195 * and found a suitable base. This is a subset of
196 * deltaSearchNonEdgeObjects.
197 */
198 public int deltasFound;
199
200 /** The total count of objects in the pack. */
201 public long totalObjects;
202
203 /**
204 * The count of objects that needed to be discovered through an object
205 * walk because they were not found in bitmap indices.
206 */
207 public long bitmapIndexMisses;
208
209 /** The total count of deltas output. */
210 public long totalDeltas;
211
212 /** The count of reused objects in the pack. */
213 public long reusedObjects;
214
215 /** The count of reused deltas in the pack. */
216 public long reusedDeltas;
217
218 /** The count of total bytes in the pack. */
219 public long totalBytes;
220
221 /** The size of the thin pack in bytes, if a thin pack was generated. */
222 public long thinPackBytes;
223
224 /** Time in ms spent counting the objects that will go into the pack. */
225 public long timeCounting;
226
227 /** Time in ms spent searching for objects to reuse. */
228 public long timeSearchingForReuse;
229
230 /** Time in ms spent searching for sizes of objects. */
231 public long timeSearchingForSizes;
232
233 /** Time in ms spent compressing the pack. */
234 public long timeCompressing;
235
236 /** Time in ms spent writing the pack. */
237 public long timeWriting;
238
239 /**
240 * Statistics about each object type in the pack (commits, tags, trees
241 * and blobs.)
242 */
243 public ObjectType.Accumulator[] objectTypes;
244
245 {
246 objectTypes = new ObjectType.Accumulator[5];
247 objectTypes[OBJ_COMMIT] = new ObjectType.Accumulator();
248 objectTypes[OBJ_TREE] = new ObjectType.Accumulator();
249 objectTypes[OBJ_BLOB] = new ObjectType.Accumulator();
250 objectTypes[OBJ_TAG] = new ObjectType.Accumulator();
251 }
252 }
253
254 private Accumulator statistics;
255
256 /**
257 * Creates a new {@link PackStatistics} object from the accumulator.
258 *
259 * @param accumulator
260 * the accumulator of the statistics
261 */
262 public PackStatistics(Accumulator accumulator) {
263 /*
264 * For efficiency this wraps and serves up the Accumulator object rather
265 * than making a deep clone. Normal usage of PackWriter is to create a
266 * single pack/index/bitmap and only call getStatistics() after all work
267 * is complete.
268 */
269 statistics = accumulator;
270 }
271
272 /**
273 * @return unmodifiable collection of objects to be included in the pack.
274 * May be {@code null} if the pack was hand-crafted in a unit test.
275 */
276 public Set<ObjectId> getInterestingObjects() {
277 return statistics.interestingObjects;
278 }
279
280 /**
281 * @return unmodifiable collection of objects that should be excluded from
282 * the pack, as the peer that will receive the pack already has
283 * these objects.
284 */
285 public Set<ObjectId> getUninterestingObjects() {
286 return statistics.uninterestingObjects;
287 }
288
289 /**
290 * @return unmodifiable collection of objects that were shallow commits on
291 * the client.
292 */
293 public Set<ObjectId> getClientShallowCommits() {
294 return statistics.clientShallowCommits;
295 }
296
297 /**
298 * @return unmodifiable list of the cached packs that were reused in the
299 * output, if any were selected for reuse.
300 */
301 public List<CachedPack> getReusedPacks() {
302 return statistics.reusedPacks;
303 }
304
305 /** @return unmodifiable collection of the root commits of the history. */
306 public Set<ObjectId> getRootCommits() {
307 return statistics.rootCommits;
308 }
309
310 /**
311 * @return number of objects in the output pack that went through the delta
312 * search process in order to find a potential delta base.
313 */
314 public int getDeltaSearchNonEdgeObjects() {
315 return statistics.deltaSearchNonEdgeObjects;
316 }
317
318 /**
319 * @return number of objects in the output pack that went through delta base
320 * search and found a suitable base. This is a subset of
321 * {@link #getDeltaSearchNonEdgeObjects()}.
322 */
323 public int getDeltasFound() {
324 return statistics.deltasFound;
325 }
326
327 /**
328 * @return total number of objects output. This total includes the value of
329 * {@link #getTotalDeltas()}.
330 */
331 public long getTotalObjects() {
332 return statistics.totalObjects;
333 }
334
335 /**
336 * @return the count of objects that needed to be discovered through an
337 * object walk because they were not found in bitmap indices.
338 * Returns -1 if no bitmap indices were found.
339 */
340 public long getBitmapIndexMisses() {
341 return statistics.bitmapIndexMisses;
342 }
343
344 /**
345 * @return total number of deltas output. This may be lower than the actual
346 * number of deltas if a cached pack was reused.
347 */
348 public long getTotalDeltas() {
349 return statistics.totalDeltas;
350 }
351
352 /**
353 * @return number of objects whose existing representation was reused in the
354 * output. This count includes {@link #getReusedDeltas()}.
355 */
356 public long getReusedObjects() {
357 return statistics.reusedObjects;
358 }
359
360 /**
361 * @return number of deltas whose existing representation was reused in the
362 * output, as their base object was also output or was assumed
363 * present for a thin pack. This may be lower than the actual number
364 * of reused deltas if a cached pack was reused.
365 */
366 public long getReusedDeltas() {
367 return statistics.reusedDeltas;
368 }
369
370 /**
371 * @return total number of bytes written. This size includes the pack
372 * header, trailer, thin pack, and reused cached pack(s).
373 */
374 public long getTotalBytes() {
375 return statistics.totalBytes;
376 }
377
378 /**
379 * @return size of the thin pack in bytes, if a thin pack was generated. A
380 * thin pack is created when the client already has objects and some
381 * deltas are created against those objects, or if a cached pack is
382 * being used and some deltas will reference objects in the cached
383 * pack. This size does not include the pack header or trailer.
384 */
385 public long getThinPackBytes() {
386 return statistics.thinPackBytes;
387 }
388
389 /**
390 * @param typeCode
391 * object type code, e.g. OBJ_COMMIT or OBJ_TREE.
392 * @return information about this type of object in the pack.
393 */
394 public ObjectType byObjectType(int typeCode) {
395 return new ObjectType(statistics.objectTypes[typeCode]);
396 }
397
398 /** @return true if the resulting pack file was a shallow pack. */
399 public boolean isShallow() {
400 return statistics.depth > 0;
401 }
402
403 /** @return depth (in commits) the pack includes if shallow. */
404 public int getDepth() {
405 return statistics.depth;
406 }
407
408 /**
409 * @return time in milliseconds spent enumerating the objects that need to
410 * be included in the output. This time includes any restarts that
411 * occur when a cached pack is selected for reuse.
412 */
413 public long getTimeCounting() {
414 return statistics.timeCounting;
415 }
416
417 /**
418 * @return time in milliseconds spent matching existing representations
419 * against objects that will be transmitted, or that the client can
420 * be assumed to already have.
421 */
422 public long getTimeSearchingForReuse() {
423 return statistics.timeSearchingForReuse;
424 }
425
426 /**
427 * @return time in milliseconds spent finding the sizes of all objects that
428 * will enter the delta compression search window. The sizes need to
429 * be known to better match similar objects together and improve
430 * delta compression ratios.
431 */
432 public long getTimeSearchingForSizes() {
433 return statistics.timeSearchingForSizes;
434 }
435
436 /**
437 * @return time in milliseconds spent on delta compression. This is observed
438 * wall-clock time and does not accurately track CPU time used when
439 * multiple threads were used to perform the delta compression.
440 */
441 public long getTimeCompressing() {
442 return statistics.timeCompressing;
443 }
444
445 /**
446 * @return time in milliseconds spent writing the pack output, from start of
447 * header until end of trailer. The transfer speed can be
448 * approximated by dividing {@link #getTotalBytes()} by this value.
449 */
450 public long getTimeWriting() {
451 return statistics.timeWriting;
452 }
453
454 /** @return total time spent processing this pack. */
455 public long getTimeTotal() {
456 return statistics.timeCounting + statistics.timeSearchingForReuse
457 + statistics.timeSearchingForSizes + statistics.timeCompressing
458 + statistics.timeWriting;
459 }
460
461 /**
462 * @return get the average output speed in terms of bytes-per-second.
463 * {@code getTotalBytes() / (getTimeWriting() / 1000.0)}.
464 */
465 public double getTransferRate() {
466 return getTotalBytes() / (getTimeWriting() / 1000.0);
467 }
468
469 /** @return formatted message string for display to clients. */
470 public String getMessage() {
471 return MessageFormat.format(JGitText.get().packWriterStatistics,
472 Long.valueOf(statistics.totalObjects),
473 Long.valueOf(statistics.totalDeltas),
474 Long.valueOf(statistics.reusedObjects),
475 Long.valueOf(statistics.reusedDeltas));
476 }
477
478 /** @return a map containing ObjectType statistics. */
479 public Map<Integer, ObjectType> getObjectTypes() {
480 HashMap<Integer, ObjectType> map = new HashMap<>();
481 map.put(Integer.valueOf(OBJ_BLOB), new ObjectType(
482 statistics.objectTypes[OBJ_BLOB]));
483 map.put(Integer.valueOf(OBJ_COMMIT), new ObjectType(
484 statistics.objectTypes[OBJ_COMMIT]));
485 map.put(Integer.valueOf(OBJ_TAG), new ObjectType(
486 statistics.objectTypes[OBJ_TAG]));
487 map.put(Integer.valueOf(OBJ_TREE), new ObjectType(
488 statistics.objectTypes[OBJ_TREE]));
489 return map;
490 }
491 }