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 }