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

Side by Side Diff: net/base/sdch_filter.cc

Issue 100004: Hand craft an A/B test of SDCH compression... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 7 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 | Annotate | Revision Log
« no previous file with comments | « net/base/sdch_filter.h ('k') | net/base/sdch_filter_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <limits.h> 5 #include <limits.h>
6 #include <ctype.h> 6 #include <ctype.h>
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/file_util.h" 9 #include "base/file_util.h"
10 #include "base/histogram.h" 10 #include "base/histogram.h"
11 #include "base/logging.h" 11 #include "base/logging.h"
12 #include "net/base/sdch_filter.h" 12 #include "net/base/sdch_filter.h"
13 #include "net/base/sdch_manager.h" 13 #include "net/base/sdch_manager.h"
14 14
15 #include "sdch/open-vcdiff/src/google/vcdecoder.h" 15 #include "sdch/open-vcdiff/src/google/vcdecoder.h"
16 16
17 SdchFilter::SdchFilter(const FilterContext& filter_context) 17 SdchFilter::SdchFilter(const FilterContext& filter_context)
18 : Filter(filter_context), 18 : Filter(filter_context),
19 decoding_status_(DECODING_UNINITIALIZED), 19 decoding_status_(DECODING_UNINITIALIZED),
20 vcdiff_streaming_decoder_(NULL), 20 vcdiff_streaming_decoder_(NULL),
21 dictionary_hash_(), 21 dictionary_hash_(),
22 dictionary_hash_is_plausible_(false), 22 dictionary_hash_is_plausible_(false),
23 dictionary_(NULL), 23 dictionary_(NULL),
24 dest_buffer_excess_(), 24 dest_buffer_excess_(),
25 dest_buffer_excess_index_(0), 25 dest_buffer_excess_index_(0),
26 source_bytes_(0), 26 source_bytes_(0),
27 output_bytes_(0), 27 output_bytes_(0),
28 observed_packet_count_(0),
29 bytes_observed_in_packets_(0),
30 final_packet_time_(),
31 possible_pass_through_(false), 28 possible_pass_through_(false),
32 connect_time_(filter_context.GetRequestTime()),
33 was_cached_(filter_context.IsCachedContent()) { 29 was_cached_(filter_context.IsCachedContent()) {
34 bool success = filter_context.GetMimeType(&mime_type_); 30 bool success = filter_context.GetMimeType(&mime_type_);
35 DCHECK(success); 31 DCHECK(success);
36 success = filter_context.GetURL(&url_); 32 success = filter_context.GetURL(&url_);
37 DCHECK(success); 33 DCHECK(success);
38 } 34 }
39 35
40 SdchFilter::~SdchFilter() { 36 SdchFilter::~SdchFilter() {
41 // All code here is for gathering stats, and can be removed when SDCH is 37 // All code here is for gathering stats, and can be removed when SDCH is
42 // considered stable. 38 // considered stable.
(...skipping 30 matching lines...) Expand all
73 UMA_HISTOGRAM_COUNTS("Sdch.UnflushedVcdiffOut", output_bytes_); 69 UMA_HISTOGRAM_COUNTS("Sdch.UnflushedVcdiffOut", output_bytes_);
74 } 70 }
75 71
76 if (was_cached_) { 72 if (was_cached_) {
77 // Not a real error, but it is useful to have this tally. 73 // Not a real error, but it is useful to have this tally.
78 // TODO(jar): Remove this stat after SDCH stability is validated. 74 // TODO(jar): Remove this stat after SDCH stability is validated.
79 SdchManager::SdchErrorRecovery(SdchManager::CACHE_DECODED); 75 SdchManager::SdchErrorRecovery(SdchManager::CACHE_DECODED);
80 return; // We don't need timing stats, and we aready got ratios. 76 return; // We don't need timing stats, and we aready got ratios.
81 } 77 }
82 78
83 if (base::Time() == connect_time_ || read_times_.empty()) {
84 // Not a real error, but it is useful to have this tally.
85 // TODO(jar): Remove this stat after SDCH stability is validated.
86 SdchManager::SdchErrorRecovery(SdchManager::MISSING_TIME_STATS);
87 UMA_HISTOGRAM_COUNTS("Sdch.MissingTimeBytesIn",
88 static_cast<int>(filter_context().GetByteReadCount()));
89 UMA_HISTOGRAM_COUNTS("Sdch.MissingTimeVcdiffIn", source_bytes_);
90 return;
91 }
92 // Since read_times_ is not empty, we can assert we have some packets times.
93 DCHECK(final_packet_time_ != base::Time());
94
95 base::TimeDelta duration = final_packet_time_ - connect_time_;
96 // We clip our logging at 10 minutes to prevent anamolous data from being
97 // considered (per suggestion from Jake Brutlag).
98 if (10 < duration.InMinutes()) {
99 SdchManager::SdchErrorRecovery(SdchManager::OVER_10_MINUTES);
100 return;
101 }
102
103 switch (decoding_status_) { 79 switch (decoding_status_) {
104 case DECODING_IN_PROGRESS: { 80 case DECODING_IN_PROGRESS: {
105 UMA_HISTOGRAM_PERCENTAGE("Sdch.Network_Decode_Ratio_a", static_cast<int>( 81 UMA_HISTOGRAM_PERCENTAGE("Sdch.Network_Decode_Ratio_a", static_cast<int>(
106 (filter_context().GetByteReadCount() * 100) / output_bytes_)); 82 (filter_context().GetByteReadCount() * 100) / output_bytes_));
107 UMA_HISTOGRAM_CLIPPED_TIMES("Sdch.Network_Decode_Latency_F_a", duration,
108 base::TimeDelta::FromMilliseconds(20),
109 base::TimeDelta::FromMinutes(10), 100);
110 UMA_HISTOGRAM_CLIPPED_TIMES("Sdch.Network_Decode_1st_To_Last_a",
111 final_packet_time_ - read_times_[0],
112 base::TimeDelta::FromMilliseconds(20),
113 base::TimeDelta::FromMinutes(10), 100);
114 if (read_times_.size() > 4) {
115 UMA_HISTOGRAM_CLIPPED_TIMES("Sdch.Network_Decode_1st_To_2nd_b",
116 read_times_[1] - read_times_[0],
117 base::TimeDelta::FromMilliseconds(10),
118 base::TimeDelta::FromSeconds(3), 100);
119 UMA_HISTOGRAM_CLIPPED_TIMES("Sdch.Network_Decode_2nd_To_3rd_b",
120 read_times_[2] - read_times_[1],
121 base::TimeDelta::FromMilliseconds(10),
122 base::TimeDelta::FromSeconds(3), 100);
123 UMA_HISTOGRAM_CLIPPED_TIMES("Sdch.Network_Decode_3rd_To_4th_b",
124 read_times_[3] - read_times_[2],
125 base::TimeDelta::FromMilliseconds(10),
126 base::TimeDelta::FromSeconds(3), 100);
127 UMA_HISTOGRAM_CLIPPED_TIMES("Sdch.Network_Decode_4th_To_5th_b",
128 read_times_[4] - read_times_[3],
129 base::TimeDelta::FromMilliseconds(10),
130 base::TimeDelta::FromSeconds(3), 100);
131 }
132 UMA_HISTOGRAM_COUNTS_100("Sdch.Network_Decode_Packets_b",
133 observed_packet_count_);
134 UMA_HISTOGRAM_COUNTS("Sdch.Network_Decode_Bytes_Processed_a",
135 static_cast<int>(filter_context().GetByteReadCount()));
136 UMA_HISTOGRAM_COUNTS("Sdch.Network_Decode_Bytes_VcdiffOut_a", 83 UMA_HISTOGRAM_COUNTS("Sdch.Network_Decode_Bytes_VcdiffOut_a",
137 output_bytes_); 84 output_bytes_);
85 filter_context().RecordPacketStats(FilterContext::SDCH_DECODE);
86
87 // Allow latency experiments to proceed.
88 SdchManager::Global()->SetAllowLatencyExperiment(url_, true);
138 return; 89 return;
139 } 90 }
140 case PASS_THROUGH: { 91 case PASS_THROUGH: {
141 UMA_HISTOGRAM_CLIPPED_TIMES("Sdch.Network_Pass-through_Latency_F_a", 92 filter_context().RecordPacketStats(FilterContext::SDCH_PASSTHROUGH);
142 duration,
143 base::TimeDelta::FromMilliseconds(20),
144 base::TimeDelta::FromMinutes(10), 100);
145 UMA_HISTOGRAM_CLIPPED_TIMES("Sdch.Network_Pass-through_1st_To_Last_a",
146 final_packet_time_ - read_times_[0],
147 base::TimeDelta::FromMilliseconds(20),
148 base::TimeDelta::FromMinutes(10), 100);
149 if (read_times_.size() > 4) {
150 UMA_HISTOGRAM_CLIPPED_TIMES("Sdch.Network_Pass-through_1st_To_2nd_b",
151 read_times_[1] - read_times_[0],
152 base::TimeDelta::FromMilliseconds(10),
153 base::TimeDelta::FromSeconds(3), 100);
154 UMA_HISTOGRAM_CLIPPED_TIMES("Sdch.Network_Pass-through_2nd_To_3rd_b",
155 read_times_[2] - read_times_[1],
156 base::TimeDelta::FromMilliseconds(10),
157 base::TimeDelta::FromSeconds(3), 100);
158 UMA_HISTOGRAM_CLIPPED_TIMES("Sdch.Network_Pass-through_3rd_To_4th_b",
159 read_times_[3] - read_times_[2],
160 base::TimeDelta::FromMilliseconds(10),
161 base::TimeDelta::FromSeconds(3), 100);
162 UMA_HISTOGRAM_CLIPPED_TIMES("Sdch.Network_Pass-through_4th_To_5th_b",
163 read_times_[4] - read_times_[3],
164 base::TimeDelta::FromMilliseconds(10),
165 base::TimeDelta::FromSeconds(3), 100);
166 }
167 UMA_HISTOGRAM_COUNTS_100("Sdch.Network_Pass-through_Packets_b",
168 observed_packet_count_);
169 return; 93 return;
170 } 94 }
171 case DECODING_UNINITIALIZED: { 95 case DECODING_UNINITIALIZED: {
172 SdchManager::SdchErrorRecovery(SdchManager::UNINITIALIZED); 96 SdchManager::SdchErrorRecovery(SdchManager::UNINITIALIZED);
173 return; 97 return;
174 } 98 }
175 case WAITING_FOR_DICTIONARY_SELECTION: { 99 case WAITING_FOR_DICTIONARY_SELECTION: {
176 SdchManager::SdchErrorRecovery(SdchManager::PRIOR_TO_DICTIONARY); 100 SdchManager::SdchErrorRecovery(SdchManager::PRIOR_TO_DICTIONARY);
177 return; 101 return;
178 } 102 }
179 case DECODING_ERROR: { 103 case DECODING_ERROR: {
180 SdchManager::SdchErrorRecovery(SdchManager::DECODE_ERROR); 104 SdchManager::SdchErrorRecovery(SdchManager::DECODE_ERROR);
181 return; 105 return;
182 } 106 }
183 case META_REFRESH_RECOVERY: { 107 case META_REFRESH_RECOVERY: {
184 // Already accounted for when set. 108 // Already accounted for when set.
185 return; 109 return;
186 } 110 }
187 } // end of switch. 111 } // end of switch.
188 } 112 }
189 113
190 void SdchFilter::UpdateReadTimes() {
191 if (!next_stream_data_ || (stream_data_len_ == 0)) {
192 // Don't update when we're called to just flush out our internal buffers.
193 return;
194 }
195 const size_t bytes_read_so_far =
196 static_cast<size_t>(filter_context().GetByteReadCount());
197 if (bytes_read_so_far <= bytes_observed_in_packets_) {
198 DCHECK(bytes_read_so_far == bytes_observed_in_packets_);
199 return; // No new bytes have arrived.
200 }
201
202 // We only save distinct times for the first 5 packets.
203 const size_t kMaxTimesInArray = 5;
204 const size_t kTypicalPacketSize = 1430;
205 final_packet_time_ = base::Time::Now();
206 while (bytes_read_so_far > bytes_observed_in_packets_) {
207 if (++observed_packet_count_ <= kMaxTimesInArray) {
208 read_times_.push_back(final_packet_time_);
209 }
210 bytes_observed_in_packets_ += kTypicalPacketSize;
211 }
212 // Since packets may not be full, we'll remember the number of bytes we've
213 // accounted for in packets thus far.
214 bytes_observed_in_packets_ = bytes_read_so_far;
215 }
216
217 bool SdchFilter::InitDecoding(Filter::FilterType filter_type) { 114 bool SdchFilter::InitDecoding(Filter::FilterType filter_type) {
218 if (decoding_status_ != DECODING_UNINITIALIZED) 115 if (decoding_status_ != DECODING_UNINITIALIZED)
219 return false; 116 return false;
220 117
221 // Handle case where sdch filter is guessed, but not required. 118 // Handle case where sdch filter is guessed, but not required.
222 if (FILTER_TYPE_SDCH_POSSIBLE == filter_type) 119 if (FILTER_TYPE_SDCH_POSSIBLE == filter_type)
223 possible_pass_through_ = true; 120 possible_pass_through_ = true;
224 121
225 // Initialize decoder only after we have a dictionary in hand. 122 // Initialize decoder only after we have a dictionary in hand.
226 decoding_status_ = WAITING_FOR_DICTIONARY_SELECTION; 123 decoding_status_ = WAITING_FOR_DICTIONARY_SELECTION;
(...skipping 11 matching lines...) Expand all
238 135
239 136
240 Filter::FilterStatus SdchFilter::ReadFilteredData(char* dest_buffer, 137 Filter::FilterStatus SdchFilter::ReadFilteredData(char* dest_buffer,
241 int* dest_len) { 138 int* dest_len) {
242 int available_space = *dest_len; 139 int available_space = *dest_len;
243 *dest_len = 0; // Nothing output yet. 140 *dest_len = 0; // Nothing output yet.
244 141
245 if (!dest_buffer || available_space <= 0) 142 if (!dest_buffer || available_space <= 0)
246 return FILTER_ERROR; 143 return FILTER_ERROR;
247 144
248 UpdateReadTimes();
249
250 if (WAITING_FOR_DICTIONARY_SELECTION == decoding_status_) { 145 if (WAITING_FOR_DICTIONARY_SELECTION == decoding_status_) {
251 FilterStatus status = InitializeDictionary(); 146 FilterStatus status = InitializeDictionary();
252 if (FILTER_NEED_MORE_DATA == status) 147 if (FILTER_NEED_MORE_DATA == status)
253 return FILTER_NEED_MORE_DATA; 148 return FILTER_NEED_MORE_DATA;
254 if (FILTER_ERROR == status) { 149 if (FILTER_ERROR == status) {
255 DCHECK(DECODING_ERROR == decoding_status_); 150 DCHECK(DECODING_ERROR == decoding_status_);
256 DCHECK(0 == dest_buffer_excess_index_); 151 DCHECK_EQ(0u, dest_buffer_excess_index_);
257 DCHECK(dest_buffer_excess_.empty()); 152 DCHECK(dest_buffer_excess_.empty());
258 // This is where we try very hard to do error recovery, and make this 153 // This is where we try very hard to do error recovery, and make this
259 // protocol robust in teh face of proxies that do many different things. 154 // protocol robust in teh face of proxies that do many different things.
260 // If we decide that things are looking very bad (too hard to recover), 155 // If we decide that things are looking very bad (too hard to recover),
261 // we may even issue a "meta-refresh" to reload the page without an SDCH 156 // we may even issue a "meta-refresh" to reload the page without an SDCH
262 // advertisement (so that we are sure we're not hurting anything). First 157 // advertisement (so that we are sure we're not hurting anything). First
263 // we try for some light weight recovery, and teh final else clause below 158 // we try for some light weight recovery, and teh final else clause below
264 // supports the last ditch meta-refresh approach. 159 // supports the last ditch meta-refresh approach.
265 // 160 //
266 // Watch out for an error page inserted by the proxy as part of a 40x 161 // Watch out for an error page inserted by the proxy as part of a 40x
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
326 } 221 }
327 } else { 222 } else {
328 DCHECK(DECODING_IN_PROGRESS == decoding_status_); 223 DCHECK(DECODING_IN_PROGRESS == decoding_status_);
329 } 224 }
330 } 225 }
331 226
332 int amount = OutputBufferExcess(dest_buffer, available_space); 227 int amount = OutputBufferExcess(dest_buffer, available_space);
333 *dest_len += amount; 228 *dest_len += amount;
334 dest_buffer += amount; 229 dest_buffer += amount;
335 available_space -= amount; 230 available_space -= amount;
336 DCHECK(available_space >= 0); 231 DCHECK_GE(available_space, 0);
337 232
338 if (available_space <= 0) 233 if (available_space <= 0)
339 return FILTER_OK; 234 return FILTER_OK;
340 DCHECK(dest_buffer_excess_.empty()); 235 DCHECK(dest_buffer_excess_.empty());
341 DCHECK(0 == dest_buffer_excess_index_); 236 DCHECK_EQ(0u, dest_buffer_excess_index_);
342 237
343 if (decoding_status_ != DECODING_IN_PROGRESS) { 238 if (decoding_status_ != DECODING_IN_PROGRESS) {
344 if (META_REFRESH_RECOVERY == decoding_status_) { 239 if (META_REFRESH_RECOVERY == decoding_status_) {
345 // Absorb all input data. We've already output page reload HTML. 240 // Absorb all input data. We've already output page reload HTML.
346 next_stream_data_ = NULL; 241 next_stream_data_ = NULL;
347 stream_data_len_ = 0; 242 stream_data_len_ = 0;
348 return FILTER_NEED_MORE_DATA; 243 return FILTER_NEED_MORE_DATA;
349 } 244 }
350 if (PASS_THROUGH == decoding_status_) { 245 if (PASS_THROUGH == decoding_status_) {
351 // We must pass in available_space, but it will be changed to bytes_used. 246 // We must pass in available_space, but it will be changed to bytes_used.
(...skipping 29 matching lines...) Expand all
381 dest_buffer += amount; 276 dest_buffer += amount;
382 available_space -= amount; 277 available_space -= amount;
383 if (0 == available_space && !dest_buffer_excess_.empty()) 278 if (0 == available_space && !dest_buffer_excess_.empty())
384 return FILTER_OK; 279 return FILTER_OK;
385 return FILTER_NEED_MORE_DATA; 280 return FILTER_NEED_MORE_DATA;
386 } 281 }
387 282
388 Filter::FilterStatus SdchFilter::InitializeDictionary() { 283 Filter::FilterStatus SdchFilter::InitializeDictionary() {
389 const size_t kServerIdLength = 9; // Dictionary hash plus null from server. 284 const size_t kServerIdLength = 9; // Dictionary hash plus null from server.
390 size_t bytes_needed = kServerIdLength - dictionary_hash_.size(); 285 size_t bytes_needed = kServerIdLength - dictionary_hash_.size();
391 DCHECK(bytes_needed > 0); 286 DCHECK_GT(bytes_needed, 0u);
392 if (!next_stream_data_) 287 if (!next_stream_data_)
393 return FILTER_NEED_MORE_DATA; 288 return FILTER_NEED_MORE_DATA;
394 if (static_cast<size_t>(stream_data_len_) < bytes_needed) { 289 if (static_cast<size_t>(stream_data_len_) < bytes_needed) {
395 dictionary_hash_.append(next_stream_data_, stream_data_len_); 290 dictionary_hash_.append(next_stream_data_, stream_data_len_);
396 next_stream_data_ = NULL; 291 next_stream_data_ = NULL;
397 stream_data_len_ = 0; 292 stream_data_len_ = 0;
398 return FILTER_NEED_MORE_DATA; 293 return FILTER_NEED_MORE_DATA;
399 } 294 }
400 dictionary_hash_.append(next_stream_data_, bytes_needed); 295 dictionary_hash_.append(next_stream_data_, bytes_needed);
401 DCHECK(kServerIdLength == dictionary_hash_.size()); 296 DCHECK(kServerIdLength == dictionary_hash_.size());
402 stream_data_len_ -= bytes_needed; 297 stream_data_len_ -= bytes_needed;
403 DCHECK(0 <= stream_data_len_); 298 DCHECK_LE(0, stream_data_len_);
404 if (stream_data_len_ > 0) 299 if (stream_data_len_ > 0)
405 next_stream_data_ += bytes_needed; 300 next_stream_data_ += bytes_needed;
406 else 301 else
407 next_stream_data_ = NULL; 302 next_stream_data_ = NULL;
408 303
409 DCHECK(!dictionary_.get()); 304 DCHECK(!dictionary_.get());
410 dictionary_hash_is_plausible_ = true; // Assume plausible, but check. 305 dictionary_hash_is_plausible_ = true; // Assume plausible, but check.
411 306
412 SdchManager::Dictionary* dictionary = NULL; 307 SdchManager::Dictionary* dictionary = NULL;
413 if ('\0' == dictionary_hash_[kServerIdLength - 1]) 308 if ('\0' == dictionary_hash_[kServerIdLength - 1])
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
453 memcpy(dest_buffer, dest_buffer_excess_.data() + dest_buffer_excess_index_, 348 memcpy(dest_buffer, dest_buffer_excess_.data() + dest_buffer_excess_index_,
454 amount); 349 amount);
455 dest_buffer_excess_index_ += amount; 350 dest_buffer_excess_index_ += amount;
456 if (dest_buffer_excess_.size() <= dest_buffer_excess_index_) { 351 if (dest_buffer_excess_.size() <= dest_buffer_excess_index_) {
457 DCHECK(dest_buffer_excess_.size() == dest_buffer_excess_index_); 352 DCHECK(dest_buffer_excess_.size() == dest_buffer_excess_index_);
458 dest_buffer_excess_.clear(); 353 dest_buffer_excess_.clear();
459 dest_buffer_excess_index_ = 0; 354 dest_buffer_excess_index_ = 0;
460 } 355 }
461 return amount; 356 return amount;
462 } 357 }
OLDNEW
« no previous file with comments | « net/base/sdch_filter.h ('k') | net/base/sdch_filter_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698