Home | History | Annotate | Download | only in net_internals
      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 var SourceEntry = (function() {
      6   'use strict';
      7 
      8   /**
      9    * A SourceEntry gathers all log entries with the same source.
     10    *
     11    * @constructor
     12    */
     13   function SourceEntry(logEntry, maxPreviousSourceId) {
     14     this.maxPreviousSourceId_ = maxPreviousSourceId;
     15     this.entries_ = [];
     16     this.description_ = '';
     17 
     18     // Set to true on most net errors.
     19     this.isError_ = false;
     20 
     21     // If the first entry is a BEGIN_PHASE, set to false.
     22     // Set to true when an END_PHASE matching the first entry is encountered.
     23     this.isInactive_ = true;
     24 
     25     if (logEntry.phase == EventPhase.PHASE_BEGIN)
     26       this.isInactive_ = false;
     27 
     28     this.update(logEntry);
     29   }
     30 
     31   SourceEntry.prototype = {
     32     update: function(logEntry) {
     33       // Only the last event should have the same type first event,
     34       if (!this.isInactive_ &&
     35           logEntry.phase == EventPhase.PHASE_END &&
     36           logEntry.type == this.entries_[0].type) {
     37         this.isInactive_ = true;
     38       }
     39 
     40       // If we have a net error code, update |this.isError_| if appropriate.
     41       if (logEntry.params) {
     42         var netErrorCode = logEntry.params.net_error;
     43         // Skip both cases where netErrorCode is undefined, and cases where it
     44         // is 0, indicating no actual error occurred.
     45         if (netErrorCode) {
     46           // Ignore error code caused by not finding an entry in the cache.
     47           if (logEntry.type != EventType.HTTP_CACHE_OPEN_ENTRY ||
     48               netErrorCode != NetError.FAILED) {
     49             this.isError_ = true;
     50           }
     51         }
     52       }
     53 
     54       var prevStartEntry = this.getStartEntry_();
     55       this.entries_.push(logEntry);
     56       var curStartEntry = this.getStartEntry_();
     57 
     58       // If we just got the first entry for this source.
     59       if (prevStartEntry != curStartEntry)
     60         this.updateDescription_();
     61     },
     62 
     63     updateDescription_: function() {
     64       var e = this.getStartEntry_();
     65       this.description_ = '';
     66       if (!e)
     67         return;
     68 
     69       if (e.source.type == EventSourceType.NONE) {
     70         // NONE is what we use for global events that aren't actually grouped
     71         // by a "source ID", so we will just stringize the event's type.
     72         this.description_ = EventTypeNames[e.type];
     73         return;
     74       }
     75 
     76       if (e.params == undefined) {
     77         return;
     78       }
     79 
     80       switch (e.source.type) {
     81         case EventSourceType.URL_REQUEST:
     82         case EventSourceType.SOCKET_STREAM:
     83         case EventSourceType.HTTP_STREAM_JOB:
     84           this.description_ = e.params.url;
     85           break;
     86         case EventSourceType.CONNECT_JOB:
     87           this.description_ = e.params.group_name;
     88           break;
     89         case EventSourceType.HOST_RESOLVER_IMPL_REQUEST:
     90         case EventSourceType.HOST_RESOLVER_IMPL_JOB:
     91         case EventSourceType.HOST_RESOLVER_IMPL_PROC_TASK:
     92           this.description_ = e.params.host;
     93           break;
     94         case EventSourceType.DISK_CACHE_ENTRY:
     95         case EventSourceType.MEMORY_CACHE_ENTRY:
     96           this.description_ = e.params.key;
     97           break;
     98         case EventSourceType.QUIC_SESSION:
     99           if (e.params.host != undefined)
    100             this.description_ = e.params.host;
    101           break;
    102         case EventSourceType.SPDY_SESSION:
    103           if (e.params.host)
    104             this.description_ = e.params.host + ' (' + e.params.proxy + ')';
    105           break;
    106         case EventSourceType.HTTP_PIPELINED_CONNECTION:
    107           if (e.params.host_and_port)
    108             this.description_ = e.params.host_and_port;
    109           break;
    110         case EventSourceType.SOCKET:
    111         case EventSourceType.PROXY_CLIENT_SOCKET:
    112           // Use description of parent source, if any.
    113           if (e.params.source_dependency != undefined) {
    114             var parentId = e.params.source_dependency.id;
    115             this.description_ =
    116                 SourceTracker.getInstance().getDescription(parentId);
    117           }
    118           break;
    119         case EventSourceType.UDP_SOCKET:
    120           if (e.params.address != undefined) {
    121             this.description_ = e.params.address;
    122             // If the parent of |this| is a HOST_RESOLVER_IMPL_JOB, use
    123             // '<DNS Server IP> [<host we're resolving>]'.
    124             if (this.entries_[0].type == EventType.SOCKET_ALIVE &&
    125                 this.entries_[0].params &&
    126                 this.entries_[0].params.source_dependency != undefined) {
    127               var parentId = this.entries_[0].params.source_dependency.id;
    128               var parent = SourceTracker.getInstance().getSourceEntry(parentId);
    129               if (parent &&
    130                   parent.getSourceType() ==
    131                       EventSourceType.HOST_RESOLVER_IMPL_JOB &&
    132                   parent.getDescription().length > 0) {
    133                 this.description_ += ' [' + parent.getDescription() + ']';
    134               }
    135             }
    136           }
    137           break;
    138         case EventSourceType.ASYNC_HOST_RESOLVER_REQUEST:
    139         case EventSourceType.DNS_TRANSACTION:
    140           this.description_ = e.params.hostname;
    141           break;
    142         case EventSourceType.DOWNLOAD:
    143           switch (e.type) {
    144             case EventType.DOWNLOAD_FILE_RENAMED:
    145               this.description_ = e.params.new_filename;
    146               break;
    147             case EventType.DOWNLOAD_FILE_OPENED:
    148               this.description_ = e.params.file_name;
    149               break;
    150             case EventType.DOWNLOAD_ITEM_ACTIVE:
    151               this.description_ = e.params.file_name;
    152               break;
    153           }
    154           break;
    155         case EventSourceType.FILESTREAM:
    156           this.description_ = e.params.file_name;
    157           break;
    158         case EventSourceType.IPV6_PROBE_JOB:
    159           if (e.type == EventType.IPV6_PROBE_RUNNING &&
    160               e.phase == EventPhase.PHASE_END) {
    161             this.description_ = e.params.ipv6_supported ? 'IPv6 Supported' :
    162                                                           'IPv6 Not Supported';
    163           }
    164           break;
    165       }
    166 
    167       if (this.description_ == undefined)
    168         this.description_ = '';
    169     },
    170 
    171     /**
    172      * Returns a description for this source log stream, which will be displayed
    173      * in the list view. Most often this is a URL that identifies the request,
    174      * or a hostname for a connect job, etc...
    175      */
    176     getDescription: function() {
    177       return this.description_;
    178     },
    179 
    180     /**
    181      * Returns the starting entry for this source. Conceptually this is the
    182      * first entry that was logged to this source. However, we skip over the
    183      * TYPE_REQUEST_ALIVE entries which wrap TYPE_URL_REQUEST_START_JOB /
    184      * TYPE_SOCKET_STREAM_CONNECT.
    185      */
    186     getStartEntry_: function() {
    187       if (this.entries_.length < 1)
    188         return undefined;
    189       if (this.entries_[0].source.type == EventSourceType.FILESTREAM) {
    190         var e = this.findLogEntryByType_(EventType.FILE_STREAM_OPEN);
    191         if (e != undefined)
    192           return e;
    193       }
    194       if (this.entries_[0].source.type == EventSourceType.DOWNLOAD) {
    195         // If any rename occurred, use the last name
    196         e = this.findLastLogEntryStartByType_(
    197             EventType.DOWNLOAD_FILE_RENAMED);
    198         if (e != undefined)
    199           return e;
    200         // Otherwise, if the file was opened, use that name
    201         e = this.findLogEntryByType_(EventType.DOWNLOAD_FILE_OPENED);
    202         if (e != undefined)
    203           return e;
    204         // History items are never opened, so use the activation info
    205         e = this.findLogEntryByType_(EventType.DOWNLOAD_ITEM_ACTIVE);
    206         if (e != undefined)
    207           return e;
    208       }
    209       if (this.entries_.length >= 2) {
    210         // Needed for compatability with log dumps prior to M26.
    211         // TODO(mmenke):  Remove this.
    212         if (this.entries_[0].type == EventType.SOCKET_POOL_CONNECT_JOB &&
    213             this.entries_[0].params == undefined) {
    214           return this.entries_[1];
    215         }
    216         if (this.entries_[1].type == EventType.UDP_CONNECT)
    217           return this.entries_[1];
    218         if (this.entries_[0].type == EventType.REQUEST_ALIVE &&
    219             this.entries_[0].params == undefined) {
    220           var startIndex = 1;
    221           // Skip over delegate events for URL_REQUESTs.
    222           for (; startIndex + 1 < this.entries_.length; ++startIndex) {
    223             var type = this.entries_[startIndex].type;
    224             if (type != EventType.URL_REQUEST_DELEGATE &&
    225                 type != EventType.DELEGATE_INFO) {
    226               break;
    227             }
    228           }
    229           return this.entries_[startIndex];
    230         }
    231         if (this.entries_[1].type == EventType.IPV6_PROBE_RUNNING)
    232           return this.entries_[1];
    233       }
    234       return this.entries_[0];
    235     },
    236 
    237     /**
    238      * Returns the first entry with the specified type, or undefined if not
    239      * found.
    240      */
    241     findLogEntryByType_: function(type) {
    242       for (var i = 0; i < this.entries_.length; ++i) {
    243         if (this.entries_[i].type == type) {
    244           return this.entries_[i];
    245         }
    246       }
    247       return undefined;
    248     },
    249 
    250     /**
    251      * Returns the beginning of the last entry with the specified type, or
    252      * undefined if not found.
    253      */
    254     findLastLogEntryStartByType_: function(type) {
    255       for (var i = this.entries_.length - 1; i >= 0; --i) {
    256         if (this.entries_[i].type == type) {
    257           if (this.entries_[i].phase != EventPhase.PHASE_END)
    258             return this.entries_[i];
    259         }
    260       }
    261       return undefined;
    262     },
    263 
    264     getLogEntries: function() {
    265       return this.entries_;
    266     },
    267 
    268     getSourceTypeString: function() {
    269       return EventSourceTypeNames[this.entries_[0].source.type];
    270     },
    271 
    272     getSourceType: function() {
    273       return this.entries_[0].source.type;
    274     },
    275 
    276     getSourceId: function() {
    277       return this.entries_[0].source.id;
    278     },
    279 
    280     /**
    281      * Returns the largest source ID seen before this object was received.
    282      * Used only for sorting SourceEntries without a source by source ID.
    283      */
    284     getMaxPreviousEntrySourceId: function() {
    285       return this.maxPreviousSourceId_;
    286     },
    287 
    288     isInactive: function() {
    289       return this.isInactive_;
    290     },
    291 
    292     isError: function() {
    293       return this.isError_;
    294     },
    295 
    296     getStartTime: function() {
    297       var startTicks = this.entries_[0].time;
    298       return timeutil.convertTimeTicksToTime(startTicks);
    299     },
    300 
    301     /**
    302      * Returns time of last event if inactive.  Returns current time otherwise.
    303      */
    304     getEndTime: function() {
    305       if (!this.isInactive_) {
    306         return timeutil.getCurrentTime();
    307       } else {
    308         var endTicks = this.entries_[this.entries_.length - 1].time;
    309         return timeutil.convertTimeTicksToTime(endTicks);
    310       }
    311     },
    312 
    313     /**
    314      * Returns the time between the first and last events with a matching
    315      * source ID.  If source is still active, uses the current time for the
    316      * last event.
    317      */
    318     getDuration: function() {
    319       var startTime = this.getStartTime();
    320       var endTime = this.getEndTime();
    321       return endTime - startTime;
    322     },
    323 
    324     /**
    325      * Prints descriptive text about |entries_| to a new node added to the end
    326      * of |parent|.
    327      */
    328     printAsText: function(parent) {
    329       // The date will be undefined if not viewing a loaded log file.
    330       printLogEntriesAsText(this.entries_, parent,
    331                             SourceTracker.getInstance().getPrivacyStripping(),
    332                             Constants.clientInfo.numericDate);
    333     }
    334   };
    335 
    336   return SourceEntry;
    337 })();
    338