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 URL_REQUEST_BLOCKED_ON_DELEGATE events for URL_REQUESTs.
    222           while (startIndex + 1 < this.entries_.length &&
    223                  this.entries_[startIndex].type ==
    224                      EventType.URL_REQUEST_BLOCKED_ON_DELEGATE) {
    225             ++startIndex;
    226           }
    227           return this.entries_[startIndex];
    228         }
    229         if (this.entries_[1].type == EventType.IPV6_PROBE_RUNNING)
    230           return this.entries_[1];
    231       }
    232       return this.entries_[0];
    233     },
    234 
    235     /**
    236      * Returns the first entry with the specified type, or undefined if not
    237      * found.
    238      */
    239     findLogEntryByType_: function(type) {
    240       for (var i = 0; i < this.entries_.length; ++i) {
    241         if (this.entries_[i].type == type) {
    242           return this.entries_[i];
    243         }
    244       }
    245       return undefined;
    246     },
    247 
    248     /**
    249      * Returns the beginning of the last entry with the specified type, or
    250      * undefined if not found.
    251      */
    252     findLastLogEntryStartByType_: function(type) {
    253       for (var i = this.entries_.length - 1; i >= 0; --i) {
    254         if (this.entries_[i].type == type) {
    255           if (this.entries_[i].phase != EventPhase.PHASE_END)
    256             return this.entries_[i];
    257         }
    258       }
    259       return undefined;
    260     },
    261 
    262     getLogEntries: function() {
    263       return this.entries_;
    264     },
    265 
    266     getSourceTypeString: function() {
    267       return EventSourceTypeNames[this.entries_[0].source.type];
    268     },
    269 
    270     getSourceType: function() {
    271       return this.entries_[0].source.type;
    272     },
    273 
    274     getSourceId: function() {
    275       return this.entries_[0].source.id;
    276     },
    277 
    278     /**
    279      * Returns the largest source ID seen before this object was received.
    280      * Used only for sorting SourceEntries without a source by source ID.
    281      */
    282     getMaxPreviousEntrySourceId: function() {
    283       return this.maxPreviousSourceId_;
    284     },
    285 
    286     isInactive: function() {
    287       return this.isInactive_;
    288     },
    289 
    290     isError: function() {
    291       return this.isError_;
    292     },
    293 
    294     getStartTime: function() {
    295       var startTicks = this.entries_[0].time;
    296       return timeutil.convertTimeTicksToTime(startTicks);
    297     },
    298 
    299     /**
    300      * Returns time of last event if inactive.  Returns current time otherwise.
    301      */
    302     getEndTime: function() {
    303       if (!this.isInactive_) {
    304         return timeutil.getCurrentTime();
    305       } else {
    306         var endTicks = this.entries_[this.entries_.length - 1].time;
    307         return timeutil.convertTimeTicksToTime(endTicks);
    308       }
    309     },
    310 
    311     /**
    312      * Returns the time between the first and last events with a matching
    313      * source ID.  If source is still active, uses the current time for the
    314      * last event.
    315      */
    316     getDuration: function() {
    317       var startTime = this.getStartTime();
    318       var endTime = this.getEndTime();
    319       return endTime - startTime;
    320     },
    321 
    322     /**
    323      * Prints descriptive text about |entries_| to a new node added to the end
    324      * of |parent|.
    325      */
    326     printAsText: function(parent) {
    327       // The date will be undefined if not viewing a loaded log file.
    328       printLogEntriesAsText(this.entries_, parent,
    329                             SourceTracker.getInstance().getPrivacyStripping(),
    330                             Constants.clientInfo.numericDate);
    331     }
    332   };
    333 
    334   return SourceEntry;
    335 })();
    336