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

Side by Side Diff: chrome/browser/net/passive_log_collector.cc

Issue 848006: Generalize the net module's LoadLog facility from a passive container, to an event stream (NetLog). (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Split up RequestTracker into ConnectJobTracker+RequestTracker+RequestTrackerBase, address comments Created 10 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 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 #ifndef NET_URL_REQUEST_REQUEST_TRACKER_H_ 5 #include "chrome/browser/net/passive_log_collector.h"
6 #define NET_URL_REQUEST_REQUEST_TRACKER_H_ 6
7 7 #include <algorithm>
8 #include <vector> 8
9 9 #include "base/string_util.h"
10 #include "base/ref_counted.h" 10 #include "chrome/browser/chrome_thread.h"
11 #include "base/linked_list.h" 11
12 #include "base/logging.h" 12 namespace {
13 #include "googleurl/src/gurl.h" 13 const size_t kMaxNumEntriesPerLog = 50;
14 #include "net/base/load_log.h" 14 const size_t kMaxConnectJobGraveyardSize = 3;
15 15 const size_t kMaxRequestGraveyardSize = 25;
16 // Class to track all of the live instances of Request associated with a 16 const size_t kMaxLiveRequests = 200;
17 // particular URLRequestContext. It keeps a circular queue of the LoadLogs 17
18 // for recently deceased requests. 18 // Sort function on source ID.
19 template<typename Request> 19 bool OrderBySourceID(const PassiveLogCollector::RequestInfo& a,
20 class RequestTracker { 20 const PassiveLogCollector::RequestInfo& b) {
21 public: 21 return a.entries[0].source.id < b.entries[0].source.id;
22 struct RecentRequestInfo { 22 }
23 GURL original_url; 23
24 scoped_refptr<net::LoadLog> load_log; 24 void AddEntryToRequestInfo(const net::NetLog::Entry& entry,
25 }; 25 bool is_unbounded,
26 26 PassiveLogCollector::RequestInfo* out_info) {
27 // Helper class to make Request insertable into a base::LinkedList, 27 // Start dropping new entries when the log has gotten too big.
28 // without making the public interface expose base::LinkNode. 28 if (out_info->entries.size() + 1 <= kMaxNumEntriesPerLog || is_unbounded) {
29 class Node : public base::LinkNode<Node> { 29 out_info->entries.push_back(entry);
30 public: 30 } else {
31 Node(Request* request) : request_(request) {} 31 out_info->num_entries_truncated += 1;
32 ~Node() {} 32 out_info->entries[kMaxNumEntriesPerLog - 1] = entry;
33 33 }
34 Request* request() const { return request_; } 34 }
35 35
36 private: 36 void AppendToRequestInfo(const PassiveLogCollector::RequestInfo& info,
37 Request* request_; 37 bool is_unbounded,
38 }; 38 PassiveLogCollector::RequestInfo* out_info) {
39 39 for (size_t i = 0; i < info.entries.size(); ++i)
40 typedef std::vector<RecentRequestInfo> RecentRequestInfoList; 40 AddEntryToRequestInfo(info.entries[i], is_unbounded, out_info);
41 typedef bool (*RecentRequestsFilterFunc)(const GURL&); 41 }
42 42
43 // The maximum number of entries for |graveyard_|, when in bounded mode. 43 } // namespace
44 static const size_t kMaxGraveyardSize; 44
45 45 //----------------------------------------------------------------------------
46 // The maximum size of URLs to stuff into RecentRequestInfo, when in bounded 46 // PassiveLogCollector
47 // mode. 47 //----------------------------------------------------------------------------
48 static const size_t kMaxGraveyardURLSize; 48
49 49 PassiveLogCollector::PassiveLogCollector()
50 // The maximum number of entries to use for LoadLogs when in bounded mode. 50 : url_request_tracker_(&connect_job_tracker_),
51 static const size_t kBoundedLoadLogMaxEntries; 51 socket_stream_tracker_(&connect_job_tracker_) {
52 52 }
53 RequestTracker() 53
54 : next_graveyard_index_(0), 54 PassiveLogCollector::~PassiveLogCollector() {
55 graveyard_filter_func_(NULL), 55 }
56 is_unbounded_(false) { 56
57 } 57 void PassiveLogCollector::OnAddEntry(const net::NetLog::Entry& entry) {
58 58 switch (entry.source.type) {
59 ~RequestTracker() {} 59 case net::NetLog::SOURCE_URL_REQUEST:
60 60 url_request_tracker_.OnAddEntry(entry);
61 // Returns a list of Requests that are alive. 61 break;
62 std::vector<Request*> GetLiveRequests() { 62 case net::NetLog::SOURCE_SOCKET_STREAM:
63 std::vector<Request*> list; 63 socket_stream_tracker_.OnAddEntry(entry);
64 for (base::LinkNode<Node>* node = live_instances_.head(); 64 break;
65 node != live_instances_.end(); 65 case net::NetLog::SOURCE_CONNECT_JOB:
66 node = node->next()) { 66 connect_job_tracker_.OnAddEntry(entry);
67 Request* request = node->value()->request(); 67 break;
68 list.push_back(request); 68 default:
69 // Drop all other logged events.
70 break;
71 }
72 }
73
74 void PassiveLogCollector::Clear() {
75 connect_job_tracker_.Clear();
76 url_request_tracker_.Clear();
77 socket_stream_tracker_.Clear();
78 }
79
80 //----------------------------------------------------------------------------
81 // RequestTrackerBase
82 //----------------------------------------------------------------------------
83
84 PassiveLogCollector::RequestTrackerBase::RequestTrackerBase(
85 size_t max_graveyard_size)
86 : max_graveyard_size_(max_graveyard_size),
87 next_graveyard_index_(0),
88 is_unbounded_(false) {
89 }
90
91 void PassiveLogCollector::RequestTrackerBase::OnAddEntry(
92 const net::NetLog::Entry& entry) {
93 RequestInfo& info = live_requests_[entry.source.id];
94 Action result = DoAddEntry(entry, &info);
95
96 switch (result) {
97 case ACTION_MOVE_TO_GRAVEYARD:
98 InsertIntoGraveyard(info);
99 // (fall-through)
100 case ACTION_DELETE:
101 RemoveFromLiveRequests(info);
102 break;
103 default:
104 break;
105 }
106
107 if (live_requests_.size() > kMaxLiveRequests) {
108 // This is a safety net in case something went wrong, to avoid continually
109 // growing memory.
110 LOG(WARNING) << "The passive log data has grown larger "
111 "than expected, resetting";
112 live_requests_.clear();
113 }
114 }
115
116 PassiveLogCollector::RequestInfoList
117 PassiveLogCollector::RequestTrackerBase::GetLiveRequests() const {
118 RequestInfoList list;
119
120 // Copy all of the live requests into the vector.
121 for (SourceIDToInfoMap::const_iterator it = live_requests_.begin();
122 it != live_requests_.end();
123 ++it) {
124 list.push_back(it->second);
125 }
126
127 std::sort(list.begin(), list.end(), OrderBySourceID);
128 return list;
129 }
130
131 void PassiveLogCollector::RequestTrackerBase::ClearRecentlyDeceased() {
132 next_graveyard_index_ = 0;
133 graveyard_.clear();
134 }
135
136 // Returns a list of recently completed Requests.
137 PassiveLogCollector::RequestInfoList
138 PassiveLogCollector::RequestTrackerBase::GetRecentlyDeceased() const {
139 RequestInfoList list;
140
141 // Copy the items from |graveyard_| (our circular queue of recently
142 // deceased request infos) into a vector, ordered from oldest to newest.
143 for (size_t i = 0; i < graveyard_.size(); ++i) {
144 size_t index = (next_graveyard_index_ + i) % graveyard_.size();
145 list.push_back(graveyard_[index]);
146 }
147 return list;
148 }
149
150 const PassiveLogCollector::RequestInfo*
151 PassiveLogCollector::RequestTrackerBase::GetRequestInfoFromGraveyard(
152 int source_id) const {
153 // Scan through the graveyard to find an entry for |source_id|.
154 for (size_t i = 0; i < graveyard_.size(); ++i) {
155 if (graveyard_[i].entries[0].source.id == source_id) {
156 return &graveyard_[i];
69 } 157 }
70 return list; 158 }
71 } 159 return NULL;
72 160 }
73 // Clears the circular buffer of RecentRequestInfos. 161
74 void ClearRecentlyDeceased() { 162 void PassiveLogCollector::RequestTrackerBase::RemoveFromLiveRequests(
75 next_graveyard_index_ = 0; 163 const RequestInfo& info) {
76 graveyard_.clear(); 164 // Remove from |live_requests_|.
77 } 165 SourceIDToInfoMap::iterator it = live_requests_.find(
78 166 info.entries[0].source.id);
79 // Returns a list of recently completed Requests. 167 DCHECK(it != live_requests_.end());
80 const RecentRequestInfoList GetRecentlyDeceased() { 168 live_requests_.erase(it);
81 RecentRequestInfoList list; 169 }
82 170
83 // Copy the items from |graveyard_| (our circular queue of recently 171 void PassiveLogCollector::RequestTrackerBase::SetUnbounded(
84 // deceased request infos) into a vector, ordered from oldest to newest. 172 bool unbounded) {
85 for (size_t i = 0; i < graveyard_.size(); ++i) { 173 // No change.
86 size_t index = (next_graveyard_index_ + i) % graveyard_.size(); 174 if (is_unbounded_ == unbounded)
87 list.push_back(graveyard_[index]); 175 return;
176
177 // If we are going from unbounded to bounded, we need to trim the
178 // graveyard. For simplicity we will simply clear it.
179 if (is_unbounded_ && !unbounded)
180 ClearRecentlyDeceased();
181
182 is_unbounded_ = unbounded;
183 }
184
185 void PassiveLogCollector::RequestTrackerBase::Clear() {
186 ClearRecentlyDeceased();
187 live_requests_.clear();
188 }
189
190 void PassiveLogCollector::RequestTrackerBase::InsertIntoGraveyard(
191 const RequestInfo& info) {
192 if (is_unbounded_) {
193 graveyard_.push_back(info);
194 return;
195 }
196
197 // Otherwise enforce a bound on the graveyard size, by treating it as a
198 // circular buffer.
199 if (graveyard_.size() < max_graveyard_size_) {
200 // Still growing to maximum capacity.
201 DCHECK_EQ(next_graveyard_index_, graveyard_.size());
202 graveyard_.push_back(info);
203 } else {
204 // At maximum capacity, overwite the oldest entry.
205 graveyard_[next_graveyard_index_] = info;
206 }
207 next_graveyard_index_ = (next_graveyard_index_ + 1) % max_graveyard_size_;
208 }
209
210 //----------------------------------------------------------------------------
211 // ConnectJobTracker
212 //----------------------------------------------------------------------------
213
214 const size_t PassiveLogCollector::ConnectJobTracker::kMaxGraveyardSize = 3;
215
216 PassiveLogCollector::ConnectJobTracker::ConnectJobTracker()
217 : RequestTrackerBase(kMaxGraveyardSize) {
218 }
219
220 PassiveLogCollector::RequestTrackerBase::Action
221 PassiveLogCollector::ConnectJobTracker::DoAddEntry(
222 const net::NetLog::Entry& entry,
223 RequestInfo* out_info) {
224 // Save the entry (possibly truncating).
225 AddEntryToRequestInfo(entry, is_unbounded(), out_info);
226
227 // If this is the end of the connect job, move the request to the graveyard.
228 if (entry.type == net::NetLog::Entry::TYPE_EVENT &&
229 entry.event.type == net::NetLog::TYPE_SOCKET_POOL_CONNECT_JOB &&
230 entry.event.phase == net::NetLog::PHASE_END) {
231 return ACTION_MOVE_TO_GRAVEYARD;
232 }
233
234 return ACTION_NONE;
235 }
236
237 //----------------------------------------------------------------------------
238 // RequestTracker
239 //----------------------------------------------------------------------------
240
241 const size_t PassiveLogCollector::RequestTracker::kMaxGraveyardSize = 25;
242 const size_t PassiveLogCollector::RequestTracker::kMaxGraveyardURLSize = 1000;
243
244 PassiveLogCollector::RequestTracker::RequestTracker(
245 ConnectJobTracker* connect_job_tracker)
246 : RequestTrackerBase(kMaxGraveyardSize),
247 connect_job_tracker_(connect_job_tracker) {
248 }
249
250 PassiveLogCollector::RequestTrackerBase::Action
251 PassiveLogCollector::RequestTracker::DoAddEntry(
252 const net::NetLog::Entry& entry,
253 RequestInfo* out_info) {
254
255 if (entry.type == net::NetLog::Entry::TYPE_EVENT &&
256 entry.event.type == net::NetLog::TYPE_SOCKET_POOL_CONNECT_JOB_ID) {
257 // If this was notification that a ConnectJob was bound to the request,
258 // copy all the logged data for that ConnectJob.
259 AddConnectJobInfo(entry, out_info);
260 } else {
261 // Otherwise just append this entry to the request info.
262 AddEntryToRequestInfo(entry, is_unbounded(), out_info);
263 }
264
265 // If this was the start of a URLRequest/SocketStream, extract the URL.
266 if (out_info->entries.size() == 1 &&
267 entry.type == net::NetLog::Entry::TYPE_EVENT &&
268 entry.event.type == net::NetLog::TYPE_REQUEST_ALIVE &&
269 entry.event.phase == net::NetLog::PHASE_BEGIN) {
270 out_info->url = entry.string;
271 out_info->entries[0].string = std::string();
272
273 // Paranoia check: truncate the URL if it is really big.
274 if (out_info->url.size() > kMaxGraveyardURLSize)
275 out_info->url = out_info->url.substr(0, kMaxGraveyardURLSize);
276 }
277
278 // If the request has ended, move it to the graveyard.
279 if (entry.type == net::NetLog::Entry::TYPE_EVENT &&
280 entry.event.type == net::NetLog::TYPE_REQUEST_ALIVE &&
281 entry.event.phase == net::NetLog::PHASE_END) {
282 if (StartsWithASCII(out_info->url, "chrome://", false)) {
283 // Avoid sending "chrome://" requests to the graveyard, since it just
284 // adds to clutter.
285 return ACTION_DELETE;
88 } 286 }
89 return list; 287 return ACTION_MOVE_TO_GRAVEYARD;
90 } 288 }
91 289
92 void Add(Request* request) { 290 return ACTION_NONE;
93 live_instances_.Append(&request->request_tracker_node_); 291 }
94 } 292
95 293 void PassiveLogCollector::RequestTracker::AddConnectJobInfo(
96 void Remove(Request* request) { 294 const net::NetLog::Entry& entry,
97 // Remove from |live_instances_|. 295 RequestInfo* live_entry) {
98 request->request_tracker_node_.RemoveFromList(); 296 // We have just been notified of which ConnectJob the
99 297 // URLRequest/SocketStream was assigned. Lookup all the data we captured
100 RecentRequestInfo info; 298 // for the ConnectJob, and append it to the URLRequest/SocketStream's
101 request->GetInfoForTracker(&info); 299 // RequestInfo.
102 300
103 if (!is_unbounded_) { 301 // TODO(eroman): This should NOT be plumbed through via |error_code| !
104 // Paranoia check: truncate |info.original_url| if it is really big. 302 int connect_job_id = entry.error_code;
105 const std::string& spec = info.original_url.possibly_invalid_spec(); 303
106 if (spec.size() > kMaxGraveyardURLSize) 304 const RequestInfo* connect_job_info =
107 info.original_url = GURL(spec.substr(0, kMaxGraveyardURLSize)); 305 connect_job_tracker_->GetRequestInfoFromGraveyard(connect_job_id);
108 } 306
109 307 if (connect_job_info) {
110 if (ShouldInsertIntoGraveyard(info)) { 308 // Append the ConnectJob information we found.
111 // Add into |graveyard_|. 309 AppendToRequestInfo(*connect_job_info, is_unbounded(), live_entry);
112 InsertIntoGraveyard(info); 310 } else {
113 } 311 // If we couldn't find the information for the ConnectJob, append a
114 } 312 // generic message instead.
115 313 net::NetLog::Entry e(entry);
116 // This function lets you exclude requests from being saved to the graveyard. 314 e.type = net::NetLog::Entry::TYPE_STRING;
117 // The graveyard is a circular buffer of the most recently completed 315 e.string = StringPrintf("Used ConnectJob id=%d", connect_job_id);
118 // requests. Pass NULL turn off filtering. Otherwise pass in a function 316 AddEntryToRequestInfo(e, is_unbounded(), live_entry);
119 // returns false to exclude requests, true otherwise. 317 }
120 void SetGraveyardFilter(RecentRequestsFilterFunc filter_func) { 318 }
121 graveyard_filter_func_ = filter_func;
122 }
123
124 bool IsUnbounded() const {
125 return is_unbounded_;
126 }
127
128 void SetUnbounded(bool unbounded) {
129 // No change.
130 if (is_unbounded_ == unbounded)
131 return;
132
133 // If we are going from unbounded to bounded, we need to trim the
134 // graveyard. For simplicity we will simply clear it.
135 if (is_unbounded_ && !unbounded)
136 ClearRecentlyDeceased();
137
138 is_unbounded_ = unbounded;
139 }
140
141 // Creates a LoadLog using the unbounded/bounded constraints that
142 // apply to this tracker.
143 net::LoadLog* CreateLoadLog() {
144 if (IsUnbounded())
145 return new net::LoadLog(net::LoadLog::kUnbounded);
146 return new net::LoadLog(kBoundedLoadLogMaxEntries);
147 }
148
149 private:
150 bool ShouldInsertIntoGraveyard(const RecentRequestInfo& info) {
151 if (!graveyard_filter_func_)
152 return true;
153 return graveyard_filter_func_(info.original_url);
154 }
155
156 void InsertIntoGraveyard(const RecentRequestInfo& info) {
157 if (is_unbounded_) {
158 graveyard_.push_back(info);
159 return;
160 }
161
162 // Otherwise enforce a bound on the graveyard size, by treating it as a
163 // circular buffer.
164 if (graveyard_.size() < kMaxGraveyardSize) {
165 // Still growing to maximum capacity.
166 DCHECK_EQ(next_graveyard_index_, graveyard_.size());
167 graveyard_.push_back(info);
168 } else {
169 // At maximum capacity, overwite the oldest entry.
170 graveyard_[next_graveyard_index_] = info;
171 }
172 next_graveyard_index_ = (next_graveyard_index_ + 1) % kMaxGraveyardSize;
173 }
174
175 base::LinkedList<Node> live_instances_;
176
177 size_t next_graveyard_index_;
178 RecentRequestInfoList graveyard_;
179 RecentRequestsFilterFunc graveyard_filter_func_;
180 bool is_unbounded_;
181 };
182
183 template<typename Request>
184 const size_t RequestTracker<Request>::kMaxGraveyardSize = 25;
185
186 template<typename Request>
187 const size_t RequestTracker<Request>::kMaxGraveyardURLSize = 1000;
188
189 template<typename Request>
190 const size_t RequestTracker<Request>::kBoundedLoadLogMaxEntries = 50;
191
192 #endif // NET_URL_REQUEST_REQUEST_TRACKER_H_
OLDNEW
« no previous file with comments | « chrome/browser/net/passive_log_collector.h ('k') | chrome/browser/net/passive_log_collector_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698