Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(44)

Side by Side Diff: tools/perf/metrics/network.py

Issue 211133004: Added telemetry test metric network.py. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@loading_metrics
Patch Set: sync to head. Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | tools/perf/metrics/network_unittest.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright 2014 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 import base64
6 import gzip
7 import hashlib
8 import io
9 import logging
10 import zlib
11
12 from metrics import Metric
13 from telemetry.page import page_measurement
14 # All network metrics are Chrome only for now.
15 from telemetry.core.backends.chrome import inspector_network
16 from telemetry.core.timeline import recording_options
17
18
19 class NetworkMetricException(page_measurement.MeasurementFailure):
20 pass
21
22
23 class HTTPResponse(object):
24 """ Represents an HTTP response from a timeline event."""
25 def __init__(self, event):
26 self._response = (
27 inspector_network.InspectorNetworkResponseData.FromTimelineEvent(event))
28 self._content_length = None
29
30 @property
31 def response(self):
32 return self._response
33
34 @property
35 def url_signature(self):
36 return hashlib.md5(self.response.url).hexdigest()
37
38 @property
39 def content_length(self):
40 if self._content_length is None:
41 self._content_length = self.GetContentLength()
42 return self._content_length
43
44 @property
45 def has_original_content_length(self):
46 return 'X-Original-Content-Length' in self.response.headers
47
48 @property
49 def original_content_length(self):
50 if self.has_original_content_length:
51 return int(self.response.GetHeader('X-Original-Content-Length'))
52 return 0
53
54 @property
55 def data_saving_rate(self):
56 if (self.response.served_from_cache or
57 not self.has_original_content_length or
58 self.original_content_length <= 0):
59 return 0.0
60 return (float(self.original_content_length - self.content_length) /
61 self.original_content_length)
62
63 def GetContentLengthFromBody(self):
64 resp = self.response
65 body, base64_encoded = resp.GetBody()
66 if not body:
67 return 0
68 # The binary data like images, etc is base64_encoded. Decode it to get
69 # the actualy content length.
70 if base64_encoded:
71 decoded = base64.b64decode(body)
72 return len(decoded)
73
74 encoding = resp.GetHeader('Content-Encoding')
75 if not encoding:
76 return len(body)
77 # The response body returned from a timeline event is always decompressed.
78 # So, we need to compress it to get the actual content length if headers
79 # say so.
80 encoding = encoding.lower()
81 if encoding == 'gzip':
82 return self.GetGizppedBodyLength(body)
83 elif encoding == 'deflate':
84 return len(zlib.compress(body, 9))
85 else:
86 raise NetworkMetricException, (
87 'Unknown Content-Encoding %s for %s' % (encoding, resp.url))
88
89 def GetContentLength(self):
90 cl = 0
91 try:
92 cl = self.GetContentLengthFromBody()
93 except Exception, e:
94 resp = self.response
95 logging.warning('Fail to get content length for %s from body: %s',
96 resp.url[:100], e)
97 cl_header = resp.GetHeader('Content-Length')
98 if cl_header:
99 cl = int(cl_header)
100 else:
101 body, _ = resp.GetBody()
102 if body:
103 cl = len(body)
104 return cl
105
106 @staticmethod
107 def GetGizppedBodyLength(body):
108 if not body:
109 return 0
110 bio = io.BytesIO()
111 try:
112 with gzip.GzipFile(fileobj=bio, mode="wb", compresslevel=9) as f:
113 f.write(body.encode('utf-8'))
114 except Exception, e:
115 logging.warning('Fail to gzip response body: %s', e)
116 raise e
117 return len(bio.getvalue())
118
119
120 class NetworkMetric(Metric):
121 """A network metric based on timeline events."""
122
123 def __init__(self):
124 super(NetworkMetric, self).__init__()
125
126 # Whether to add detailed result for each sub-resource in a page.
127 self.add_result_for_resource = False
128 self.compute_data_saving = False
129 self._events = None
130
131 def Start(self, page, tab):
132 self._events = None
133 opts = recording_options.TimelineRecordingOptions()
134 opts.record_network = True
135 tab.StartTimelineRecording(opts)
136
137 def Stop(self, page, tab):
138 assert self._events is None
139 tab.StopTimelineRecording()
140
141 def IterResponses(self, tab):
142 if self._events is None:
143 self._events = tab.timeline_model.GetAllEventsOfName('HTTPResponse')
144 if len(self._events) == 0:
145 return
146 for e in self._events:
147 yield self.ResponseFromEvent(e)
148
149 def ResponseFromEvent(self, event):
150 return HTTPResponse(event)
151
152 def AddResults(self, tab, results):
153 content_length = 0
154 original_content_length = 0
155
156 for resp in self.IterResponses(tab):
157 # Ignore content length calculation for cache hit.
158 if resp.response.served_from_cache:
159 continue
160
161 resource = resp.response.url
162 resource_signature = resp.url_signature
163 cl = resp.content_length
164 if resp.has_original_content_length:
165 ocl = resp.original_content_length
166 if ocl < cl:
167 logging.warning('original content length (%d) is less than content '
168 'lenght(%d) for resource %s', ocl, cl, resource)
169 if self.add_result_for_resource:
170 results.Add('resource_data_saving_' + resource_signature,
171 'percent', resp.data_saving_rate * 100)
172 results.Add('resource_original_content_length_' + resource_signature,
173 'bytes', ocl)
174 original_content_length += ocl
175 else:
176 original_content_length += cl
177 if self.add_result_for_resource:
178 results.Add(
179 'resource_content_length_' + resource_signature, 'bytes', cl)
180 content_length += cl
181
182 results.Add('content_length', 'bytes', content_length)
183 results.Add('original_content_length', 'bytes', original_content_length)
184 if self.compute_data_saving:
185 if (original_content_length > 0 and
186 original_content_length >= content_length):
187 saving = (float(original_content_length-content_length) * 100 /
188 original_content_length)
189 results.Add('data_saving', 'percent', saving)
190 else:
191 results.Add('data_saving', 'percent', 0.0)
OLDNEW
« no previous file with comments | « no previous file | tools/perf/metrics/network_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698