• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • 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
5var 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    /**
297     * Returns time ticks of first event.
298     */
299    getStartTicks: function() {
300      return this.entries_[0].time;
301    },
302
303    /**
304     * Returns time of last event if inactive.  Returns current time otherwise.
305     * Returned time is a "time ticks" value.
306     */
307    getEndTicks: function() {
308      if (!this.isInactive_)
309        return timeutil.getCurrentTimeTicks();
310      return this.entries_[this.entries_.length - 1].time;
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.getStartTicks();
320      var endTime = this.getEndTicks();
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      var tablePrinter = this.createTablePrinter();
330
331      // Format the table for fixed-width text.
332      tablePrinter.toText(0, parent);
333    },
334
335    /**
336     * Creates a table printer for the SourceEntry.
337     */
338    createTablePrinter: function() {
339      return createLogEntryTablePrinter(
340          this.entries_,
341          SourceTracker.getInstance().getPrivacyStripping(),
342          SourceTracker.getInstance().getUseRelativeTimes() ?
343              timeutil.getBaseTime() : 0,
344          Constants.clientInfo.numericDate);
345    },
346  };
347
348  return SourceEntry;
349})();
350