Home | History | Annotate | Download | only in download
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/browser/download/download_stats.h"
      6 
      7 #include "base/metrics/histogram.h"
      8 #include "base/strings/string_util.h"
      9 #include "content/browser/download/download_resource_handler.h"
     10 #include "content/public/browser/download_interrupt_reasons.h"
     11 #include "net/http/http_content_disposition.h"
     12 
     13 namespace content {
     14 
     15 namespace {
     16 
     17 // All possible error codes from the network module. Note that the error codes
     18 // are all positive (since histograms expect positive sample values).
     19 const int kAllInterruptReasonCodes[] = {
     20 #define INTERRUPT_REASON(label, value) (value),
     21 #include "content/public/browser/download_interrupt_reason_values.h"
     22 #undef INTERRUPT_REASON
     23 };
     24 
     25 // These values are based on net::HttpContentDisposition::ParseResult values.
     26 // Values other than HEADER_PRESENT and IS_VALID are only measured if |IS_VALID|
     27 // is true.
     28 enum ContentDispositionCountTypes {
     29   // Count of downloads which had a Content-Disposition headers. The total
     30   // number of downloads is measured by UNTHROTTLED_COUNT.
     31   CONTENT_DISPOSITION_HEADER_PRESENT = 0,
     32 
     33   // At least one of 'name', 'filename' or 'filenae*' attributes were valid and
     34   // yielded a non-empty filename.
     35   CONTENT_DISPOSITION_IS_VALID,
     36 
     37   // The following enum values correspond to
     38   // net::HttpContentDisposition::ParseResult.
     39   CONTENT_DISPOSITION_HAS_DISPOSITION_TYPE,
     40   CONTENT_DISPOSITION_HAS_UNKNOWN_TYPE,
     41   CONTENT_DISPOSITION_HAS_NAME,
     42   CONTENT_DISPOSITION_HAS_FILENAME,
     43   CONTENT_DISPOSITION_HAS_EXT_FILENAME,
     44   CONTENT_DISPOSITION_HAS_NON_ASCII_STRINGS,
     45   CONTENT_DISPOSITION_HAS_PERCENT_ENCODED_STRINGS,
     46   CONTENT_DISPOSITION_HAS_RFC2047_ENCODED_STRINGS,
     47 
     48   // Only have the 'name' attribute is present.
     49   CONTENT_DISPOSITION_HAS_NAME_ONLY,
     50 
     51   CONTENT_DISPOSITION_LAST_ENTRY
     52 };
     53 
     54 void RecordContentDispositionCount(ContentDispositionCountTypes type,
     55                                    bool record) {
     56   if (!record)
     57     return;
     58   UMA_HISTOGRAM_ENUMERATION(
     59       "Download.ContentDisposition", type, CONTENT_DISPOSITION_LAST_ENTRY);
     60 }
     61 
     62 void RecordContentDispositionCountFlag(
     63     ContentDispositionCountTypes type,
     64     int flags_to_test,
     65     net::HttpContentDisposition::ParseResultFlags flag) {
     66   RecordContentDispositionCount(type, (flags_to_test & flag) == flag);
     67 }
     68 
     69 } // namespace
     70 
     71 void RecordDownloadCount(DownloadCountTypes type) {
     72   UMA_HISTOGRAM_ENUMERATION(
     73       "Download.Counts", type, DOWNLOAD_COUNT_TYPES_LAST_ENTRY);
     74 }
     75 
     76 void RecordDownloadSource(DownloadSource source) {
     77   UMA_HISTOGRAM_ENUMERATION(
     78       "Download.Sources", source, DOWNLOAD_SOURCE_LAST_ENTRY);
     79 }
     80 
     81 void RecordDownloadCompleted(const base::TimeTicks& start, int64 download_len) {
     82   RecordDownloadCount(COMPLETED_COUNT);
     83   UMA_HISTOGRAM_LONG_TIMES("Download.Time", (base::TimeTicks::Now() - start));
     84   int64 max = 1024 * 1024 * 1024;  // One Terabyte.
     85   download_len /= 1024;  // In Kilobytes
     86   UMA_HISTOGRAM_CUSTOM_COUNTS("Download.DownloadSize",
     87                               download_len,
     88                               1,
     89                               max,
     90                               256);
     91 }
     92 
     93 void RecordDownloadInterrupted(DownloadInterruptReason reason,
     94                                int64 received,
     95                                int64 total) {
     96   RecordDownloadCount(INTERRUPTED_COUNT);
     97   UMA_HISTOGRAM_CUSTOM_ENUMERATION(
     98       "Download.InterruptedReason",
     99       reason,
    100       base::CustomHistogram::ArrayToCustomRanges(
    101           kAllInterruptReasonCodes, arraysize(kAllInterruptReasonCodes)));
    102 
    103   // The maximum should be 2^kBuckets, to have the logarithmic bucket
    104   // boundaries fall on powers of 2.
    105   static const int kBuckets = 30;
    106   static const int64 kMaxKb = 1 << kBuckets;  // One Terabyte, in Kilobytes.
    107   int64 delta_bytes = total - received;
    108   bool unknown_size = total <= 0;
    109   int64 received_kb = received / 1024;
    110   int64 total_kb = total / 1024;
    111   UMA_HISTOGRAM_CUSTOM_COUNTS("Download.InterruptedReceivedSizeK",
    112                               received_kb,
    113                               1,
    114                               kMaxKb,
    115                               kBuckets);
    116   if (!unknown_size) {
    117     UMA_HISTOGRAM_CUSTOM_COUNTS("Download.InterruptedTotalSizeK",
    118                                 total_kb,
    119                                 1,
    120                                 kMaxKb,
    121                                 kBuckets);
    122     if (delta_bytes == 0) {
    123       RecordDownloadCount(INTERRUPTED_AT_END_COUNT);
    124       UMA_HISTOGRAM_CUSTOM_ENUMERATION(
    125           "Download.InterruptedAtEndReason",
    126           reason,
    127           base::CustomHistogram::ArrayToCustomRanges(
    128               kAllInterruptReasonCodes,
    129               arraysize(kAllInterruptReasonCodes)));
    130     } else if (delta_bytes > 0) {
    131       UMA_HISTOGRAM_CUSTOM_COUNTS("Download.InterruptedOverrunBytes",
    132                                   delta_bytes,
    133                                   1,
    134                                   kMaxKb,
    135                                   kBuckets);
    136     } else {
    137       UMA_HISTOGRAM_CUSTOM_COUNTS("Download.InterruptedUnderrunBytes",
    138                                   -delta_bytes,
    139                                   1,
    140                                   kMaxKb,
    141                                   kBuckets);
    142     }
    143   }
    144 
    145   UMA_HISTOGRAM_BOOLEAN("Download.InterruptedUnknownSize", unknown_size);
    146 }
    147 
    148 void RecordDangerousDownloadAccept(DownloadDangerType danger_type) {
    149   UMA_HISTOGRAM_ENUMERATION("Download.DangerousDownloadValidated",
    150                             danger_type,
    151                             DOWNLOAD_DANGER_TYPE_MAX);
    152 }
    153 
    154 void RecordDangerousDownloadDiscard(DownloadDiscardReason reason,
    155                                     DownloadDangerType danger_type) {
    156   switch (reason) {
    157     case DOWNLOAD_DISCARD_DUE_TO_USER_ACTION:
    158       UMA_HISTOGRAM_ENUMERATION(
    159           "Download.UserDiscard", danger_type, DOWNLOAD_DANGER_TYPE_MAX);
    160       break;
    161     case DOWNLOAD_DISCARD_DUE_TO_SHUTDOWN:
    162       UMA_HISTOGRAM_ENUMERATION(
    163           "Download.Discard", danger_type, DOWNLOAD_DANGER_TYPE_MAX);
    164       break;
    165     default:
    166       NOTREACHED();
    167   }
    168 }
    169 
    170 void RecordDownloadWriteSize(size_t data_len) {
    171   int max = 1024 * 1024;  // One Megabyte.
    172   UMA_HISTOGRAM_CUSTOM_COUNTS("Download.WriteSize", data_len, 1, max, 256);
    173 }
    174 
    175 void RecordDownloadWriteLoopCount(int count) {
    176   UMA_HISTOGRAM_ENUMERATION("Download.WriteLoopCount", count, 20);
    177 }
    178 
    179 void RecordAcceptsRanges(const std::string& accepts_ranges,
    180                          int64 download_len,
    181                          const std::string& etag) {
    182   int64 max = 1024 * 1024 * 1024;  // One Terabyte.
    183   download_len /= 1024;  // In Kilobytes
    184   static const int kBuckets = 50;
    185 
    186   if (LowerCaseEqualsASCII(accepts_ranges, "none")) {
    187     UMA_HISTOGRAM_CUSTOM_COUNTS("Download.AcceptRangesNone.KBytes",
    188                                 download_len,
    189                                 1,
    190                                 max,
    191                                 kBuckets);
    192   } else if (LowerCaseEqualsASCII(accepts_ranges, "bytes")) {
    193     UMA_HISTOGRAM_CUSTOM_COUNTS("Download.AcceptRangesBytes.KBytes",
    194                                 download_len,
    195                                 1,
    196                                 max,
    197                                 kBuckets);
    198     // ETags that start with "W/" are considered weak ETags which don't imply
    199     // byte-wise equality.
    200     if (!StartsWithASCII(etag, "w/", false))
    201       RecordDownloadCount(STRONG_ETAG_AND_ACCEPTS_RANGES);
    202   } else {
    203     UMA_HISTOGRAM_CUSTOM_COUNTS("Download.AcceptRangesMissingOrInvalid.KBytes",
    204                                 download_len,
    205                                 1,
    206                                 max,
    207                                 kBuckets);
    208   }
    209 }
    210 
    211 namespace {
    212 
    213 enum DownloadContent {
    214   DOWNLOAD_CONTENT_UNRECOGNIZED = 0,
    215   DOWNLOAD_CONTENT_TEXT = 1,
    216   DOWNLOAD_CONTENT_IMAGE = 2,
    217   DOWNLOAD_CONTENT_AUDIO = 3,
    218   DOWNLOAD_CONTENT_VIDEO = 4,
    219   DOWNLOAD_CONTENT_OCTET_STREAM = 5,
    220   DOWNLOAD_CONTENT_PDF = 6,
    221   DOWNLOAD_CONTENT_DOC = 7,
    222   DOWNLOAD_CONTENT_XLS = 8,
    223   DOWNLOAD_CONTENT_PPT = 9,
    224   DOWNLOAD_CONTENT_ARCHIVE = 10,
    225   DOWNLOAD_CONTENT_EXE = 11,
    226   DOWNLOAD_CONTENT_DMG = 12,
    227   DOWNLOAD_CONTENT_CRX = 13,
    228   DOWNLOAD_CONTENT_MAX = 14,
    229 };
    230 
    231 struct MimeTypeToDownloadContent {
    232   const char* mime_type;
    233   DownloadContent download_content;
    234 };
    235 
    236 static MimeTypeToDownloadContent kMapMimeTypeToDownloadContent[] = {
    237   {"application/octet-stream", DOWNLOAD_CONTENT_OCTET_STREAM},
    238   {"binary/octet-stream", DOWNLOAD_CONTENT_OCTET_STREAM},
    239   {"application/pdf", DOWNLOAD_CONTENT_PDF},
    240   {"application/msword", DOWNLOAD_CONTENT_DOC},
    241   {"application/vnd.ms-excel", DOWNLOAD_CONTENT_XLS},
    242   {"application/vns.ms-powerpoint", DOWNLOAD_CONTENT_PPT},
    243   {"application/zip", DOWNLOAD_CONTENT_ARCHIVE},
    244   {"application/x-gzip", DOWNLOAD_CONTENT_ARCHIVE},
    245   {"application/x-rar-compressed", DOWNLOAD_CONTENT_ARCHIVE},
    246   {"application/x-tar", DOWNLOAD_CONTENT_ARCHIVE},
    247   {"application/x-bzip", DOWNLOAD_CONTENT_ARCHIVE},
    248   {"application/x-exe", DOWNLOAD_CONTENT_EXE},
    249   {"application/x-apple-diskimage", DOWNLOAD_CONTENT_DMG},
    250   {"application/x-chrome-extension", DOWNLOAD_CONTENT_CRX},
    251 };
    252 
    253 enum DownloadImage {
    254   DOWNLOAD_IMAGE_UNRECOGNIZED = 0,
    255   DOWNLOAD_IMAGE_GIF = 1,
    256   DOWNLOAD_IMAGE_JPEG = 2,
    257   DOWNLOAD_IMAGE_PNG = 3,
    258   DOWNLOAD_IMAGE_TIFF = 4,
    259   DOWNLOAD_IMAGE_ICON = 5,
    260   DOWNLOAD_IMAGE_WEBP = 6,
    261   DOWNLOAD_IMAGE_MAX = 7,
    262 };
    263 
    264 struct MimeTypeToDownloadImage {
    265   const char* mime_type;
    266   DownloadImage download_image;
    267 };
    268 
    269 static MimeTypeToDownloadImage kMapMimeTypeToDownloadImage[] = {
    270   {"image/gif", DOWNLOAD_IMAGE_GIF},
    271   {"image/jpeg", DOWNLOAD_IMAGE_JPEG},
    272   {"image/png", DOWNLOAD_IMAGE_PNG},
    273   {"image/tiff", DOWNLOAD_IMAGE_TIFF},
    274   {"image/vnd.microsoft.icon", DOWNLOAD_IMAGE_ICON},
    275   {"image/webp", DOWNLOAD_IMAGE_WEBP},
    276 };
    277 
    278 void RecordDownloadImageType(const std::string& mime_type_string) {
    279   DownloadImage download_image = DOWNLOAD_IMAGE_UNRECOGNIZED;
    280 
    281   // Look up exact matches.
    282   for (size_t i = 0; i < arraysize(kMapMimeTypeToDownloadImage); ++i) {
    283     const MimeTypeToDownloadImage& entry = kMapMimeTypeToDownloadImage[i];
    284     if (mime_type_string == entry.mime_type) {
    285       download_image = entry.download_image;
    286       break;
    287     }
    288   }
    289 
    290   UMA_HISTOGRAM_ENUMERATION("Download.ContentImageType",
    291                             download_image,
    292                             DOWNLOAD_IMAGE_MAX);
    293 }
    294 
    295 }  // namespace
    296 
    297 void RecordDownloadMimeType(const std::string& mime_type_string) {
    298   DownloadContent download_content = DOWNLOAD_CONTENT_UNRECOGNIZED;
    299 
    300   // Look up exact matches.
    301   for (size_t i = 0; i < arraysize(kMapMimeTypeToDownloadContent); ++i) {
    302     const MimeTypeToDownloadContent& entry = kMapMimeTypeToDownloadContent[i];
    303     if (mime_type_string == entry.mime_type) {
    304       download_content = entry.download_content;
    305       break;
    306     }
    307   }
    308 
    309   // Do partial matches.
    310   if (download_content == DOWNLOAD_CONTENT_UNRECOGNIZED) {
    311     if (StartsWithASCII(mime_type_string, "text/", true)) {
    312       download_content = DOWNLOAD_CONTENT_TEXT;
    313     } else if (StartsWithASCII(mime_type_string, "image/", true)) {
    314       download_content = DOWNLOAD_CONTENT_IMAGE;
    315       RecordDownloadImageType(mime_type_string);
    316     } else if (StartsWithASCII(mime_type_string, "audio/", true)) {
    317       download_content = DOWNLOAD_CONTENT_AUDIO;
    318     } else if (StartsWithASCII(mime_type_string, "video/", true)) {
    319       download_content = DOWNLOAD_CONTENT_VIDEO;
    320     }
    321   }
    322 
    323   // Record the value.
    324   UMA_HISTOGRAM_ENUMERATION("Download.ContentType",
    325                             download_content,
    326                             DOWNLOAD_CONTENT_MAX);
    327 }
    328 
    329 void RecordDownloadContentDisposition(
    330     const std::string& content_disposition_string) {
    331   if (content_disposition_string.empty())
    332     return;
    333   net::HttpContentDisposition content_disposition(content_disposition_string,
    334                                                   std::string());
    335   int result = content_disposition.parse_result_flags();
    336 
    337   bool is_valid = !content_disposition.filename().empty();
    338   RecordContentDispositionCount(CONTENT_DISPOSITION_HEADER_PRESENT, true);
    339   RecordContentDispositionCount(CONTENT_DISPOSITION_IS_VALID, is_valid);
    340   if (!is_valid)
    341     return;
    342 
    343   RecordContentDispositionCountFlag(
    344       CONTENT_DISPOSITION_HAS_DISPOSITION_TYPE, result,
    345       net::HttpContentDisposition::HAS_DISPOSITION_TYPE);
    346   RecordContentDispositionCountFlag(
    347       CONTENT_DISPOSITION_HAS_UNKNOWN_TYPE, result,
    348       net::HttpContentDisposition::HAS_UNKNOWN_DISPOSITION_TYPE);
    349   RecordContentDispositionCountFlag(
    350       CONTENT_DISPOSITION_HAS_NAME, result,
    351       net::HttpContentDisposition::HAS_NAME);
    352   RecordContentDispositionCountFlag(
    353       CONTENT_DISPOSITION_HAS_FILENAME, result,
    354       net::HttpContentDisposition::HAS_FILENAME);
    355   RecordContentDispositionCountFlag(
    356       CONTENT_DISPOSITION_HAS_EXT_FILENAME, result,
    357       net::HttpContentDisposition::HAS_EXT_FILENAME);
    358   RecordContentDispositionCountFlag(
    359       CONTENT_DISPOSITION_HAS_NON_ASCII_STRINGS, result,
    360       net::HttpContentDisposition::HAS_NON_ASCII_STRINGS);
    361   RecordContentDispositionCountFlag(
    362       CONTENT_DISPOSITION_HAS_PERCENT_ENCODED_STRINGS, result,
    363       net::HttpContentDisposition::HAS_PERCENT_ENCODED_STRINGS);
    364   RecordContentDispositionCountFlag(
    365       CONTENT_DISPOSITION_HAS_RFC2047_ENCODED_STRINGS, result,
    366       net::HttpContentDisposition::HAS_RFC2047_ENCODED_STRINGS);
    367 
    368   RecordContentDispositionCount(
    369       CONTENT_DISPOSITION_HAS_NAME_ONLY,
    370       (result & (net::HttpContentDisposition::HAS_NAME |
    371                  net::HttpContentDisposition::HAS_FILENAME |
    372                  net::HttpContentDisposition::HAS_EXT_FILENAME)) ==
    373       net::HttpContentDisposition::HAS_NAME);
    374 }
    375 
    376 void RecordFileThreadReceiveBuffers(size_t num_buffers) {
    377     UMA_HISTOGRAM_CUSTOM_COUNTS(
    378       "Download.FileThreadReceiveBuffers", num_buffers, 1,
    379       100, 100);
    380 }
    381 
    382 void RecordBandwidth(double actual_bandwidth, double potential_bandwidth) {
    383   UMA_HISTOGRAM_CUSTOM_COUNTS(
    384       "Download.ActualBandwidth", actual_bandwidth, 1, 1000000000, 50);
    385   UMA_HISTOGRAM_CUSTOM_COUNTS(
    386       "Download.PotentialBandwidth", potential_bandwidth, 1, 1000000000, 50);
    387   UMA_HISTOGRAM_PERCENTAGE(
    388       "Download.BandwidthUsed",
    389       (int) ((actual_bandwidth * 100)/ potential_bandwidth));
    390 }
    391 
    392 void RecordOpen(const base::Time& end, bool first) {
    393   if (!end.is_null()) {
    394     UMA_HISTOGRAM_LONG_TIMES("Download.OpenTime", (base::Time::Now() - end));
    395     if (first) {
    396       UMA_HISTOGRAM_LONG_TIMES("Download.FirstOpenTime",
    397                               (base::Time::Now() - end));
    398     }
    399   }
    400 }
    401 
    402 void RecordClearAllSize(int size) {
    403   UMA_HISTOGRAM_CUSTOM_COUNTS("Download.ClearAllSize",
    404                               size,
    405                               0/*min*/,
    406                               (1 << 10)/*max*/,
    407                               32/*num_buckets*/);
    408 }
    409 
    410 void RecordOpensOutstanding(int size) {
    411   UMA_HISTOGRAM_CUSTOM_COUNTS("Download.OpensOutstanding",
    412                               size,
    413                               0/*min*/,
    414                               (1 << 10)/*max*/,
    415                               64/*num_buckets*/);
    416 }
    417 
    418 void RecordContiguousWriteTime(base::TimeDelta time_blocked) {
    419   UMA_HISTOGRAM_TIMES("Download.FileThreadBlockedTime", time_blocked);
    420 }
    421 
    422 // Record what percentage of the time we have the network flow controlled.
    423 void RecordNetworkBlockage(base::TimeDelta resource_handler_lifetime,
    424                            base::TimeDelta resource_handler_blocked_time) {
    425   int percentage = 0;
    426   // Avoid division by zero errors.
    427   if (resource_handler_blocked_time != base::TimeDelta()) {
    428     percentage =
    429         resource_handler_blocked_time * 100 / resource_handler_lifetime;
    430   }
    431 
    432   UMA_HISTOGRAM_COUNTS_100("Download.ResourceHandlerBlockedPercentage",
    433                            percentage);
    434 }
    435 
    436 void RecordFileBandwidth(size_t length,
    437                          base::TimeDelta disk_write_time,
    438                          base::TimeDelta elapsed_time) {
    439   size_t elapsed_time_ms = elapsed_time.InMilliseconds();
    440   if (0u == elapsed_time_ms)
    441     elapsed_time_ms = 1;
    442   size_t disk_write_time_ms = disk_write_time.InMilliseconds();
    443   if (0u == disk_write_time_ms)
    444     disk_write_time_ms = 1;
    445 
    446   UMA_HISTOGRAM_CUSTOM_COUNTS(
    447       "Download.BandwidthOverallBytesPerSecond",
    448       (1000 * length / elapsed_time_ms), 1, 50000000, 50);
    449   UMA_HISTOGRAM_CUSTOM_COUNTS(
    450       "Download.BandwidthDiskBytesPerSecond",
    451       (1000 * length / disk_write_time_ms), 1, 50000000, 50);
    452   UMA_HISTOGRAM_COUNTS_100("Download.DiskBandwidthUsedPercentage",
    453                            disk_write_time_ms * 100 / elapsed_time_ms);
    454 }
    455 
    456 void RecordSavePackageEvent(SavePackageEvent event) {
    457   UMA_HISTOGRAM_ENUMERATION("Download.SavePackage",
    458                             event,
    459                             SAVE_PACKAGE_LAST_ENTRY);
    460 }
    461 
    462 }  // namespace content
    463