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

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

Powered by Google App Engine
This is Rietveld 408576698