Index: content/browser/stats_collection_message_filter.cc |
diff --git a/content/browser/stats_collection_message_filter.cc b/content/browser/stats_collection_message_filter.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ef40bacc223f8ca40c4b1750e8ead89dd2fe3d22 |
--- /dev/null |
+++ b/content/browser/stats_collection_message_filter.cc |
@@ -0,0 +1,189 @@ |
+// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "content/browser/stats_collection_message_filter.h" |
+ |
+#include <map> |
+#include <set> |
+ |
+#include "base/memory/singleton.h" |
+#include "base/process_util.h" |
+#include "content/common/child_process_messages.h" |
+#include "content/public/browser/notification_registrar.h" |
+#include "content/public/browser/notification_service.h" |
+#include "content/public/browser/notification_types.h" |
+#include "content/public/browser/render_process_host.h" |
+#include "content/public/browser/web_contents.h" |
+ |
+namespace { |
jam
2013/04/04 17:28:39
ditto re putting this inside content namespace
jeremy
2013/04/07 14:51:54
Done.
|
+ |
+bool RoutingIDFromNotificationSource( |
+ const content::NotificationSource& source, |
+ int *routing_id) { |
+ content::NavigationController* tab = |
+ content::Source<content::NavigationController>(source).ptr(); |
+ if (!tab) |
+ return false; |
+ content::WebContents* contents = tab->GetWebContents(); // Can never be NULL. |
jam
2013/04/04 17:28:39
nit: comment is redundant with code
jeremy
2013/04/07 14:51:54
Done.
|
+ *routing_id = contents->GetRoutingID(); |
+ return true; |
+} |
+ |
+} // namespace |
+ |
+namespace content { |
+ |
+// Singleton that listens for tab lifetime events (load/unload/crash) and |
+// records timing information. |
+// |
+// Lifetime: Needs to be instantiated before first page load in order to be able |
+// to listen to the relevant events. |
+// Once instantiated, the class will collect tab timing information for all |
+// loded tabs, GetTimingInformation() is used to read out the data. |
+class InitialLoadObserver : public content::NotificationObserver { |
+ public: |
+ static InitialLoadObserver* GetInstance() { |
+ return Singleton<InitialLoadObserver>::get(); |
+ } |
+ |
+ // content::NotificationObserver implementation: |
+ virtual void Observe(int type, |
+ const content::NotificationSource& source, |
+ const content::NotificationDetails& details) OVERRIDE; |
+ |
+ // Gets stats on tab load start/stop time given a specific |routing_id|. |
+ void GetTimingInformationForTab(int routing_id, |
+ TabLoadTime* result, |
+ base::TimeTicks* timer_start) const; |
+ |
+ private: |
+ friend struct DefaultSingletonTraits<InitialLoadObserver>; |
+ |
+ typedef std::map<int, TabLoadTime> TabTimeMap; |
+ typedef std::set<int> TabSet; |
+ |
+ InitialLoadObserver(); |
+ virtual ~InitialLoadObserver(); |
+ |
+ content::NotificationRegistrar registrar_; |
+ size_t crashed_tab_count_; |
jam
2013/04/04 17:28:39
i don't see this used other than to collect?
jeremy
2013/04/07 14:51:54
Removed.
|
+ base::TimeTicks init_time_; |
+ TabTimeMap loading_tabs_; |
+ TabSet finished_tabs_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(InitialLoadObserver); |
+}; |
+ |
+InitialLoadObserver::InitialLoadObserver() |
+ : crashed_tab_count_(0), |
+ init_time_(base::TimeTicks::Now()) { |
+ registrar_.Add(this, content::NOTIFICATION_LOAD_START, |
+ content::NotificationService::AllSources()); |
+ registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, |
jam
2013/04/04 17:28:39
so, it seems that this class is listening for LOAD
jeremy
2013/04/07 14:51:54
This makes sense but I fear I'm going to need to a
jam
2013/04/07 21:14:04
Can you elaborate on what you think you'll need?
|
+ content::NotificationService::AllSources()); |
+ registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, |
+ content::NotificationService::AllSources()); |
+} |
+ |
+InitialLoadObserver::~InitialLoadObserver() { |
+} |
+ |
+void InitialLoadObserver::Observe(int type, |
+ const content::NotificationSource& source, |
+ const content::NotificationDetails& details) { |
+ if (type == content::NOTIFICATION_LOAD_START) { |
+ int routing_id = 0; |
+ if (!RoutingIDFromNotificationSource(source, &routing_id)) |
+ return; |
+ loading_tabs_[routing_id] = TabLoadTime(base::TimeTicks::Now()); |
+ } else if (type == content::NOTIFICATION_LOAD_STOP) { |
+ int routing_id = 0; |
+ if (!RoutingIDFromNotificationSource(source, &routing_id)) |
+ return; |
+ TabTimeMap::iterator iter = loading_tabs_.find(routing_id); |
+ if (iter != loading_tabs_.end()) { |
+ finished_tabs_.insert(routing_id); |
+ iter->second.set_stop_time(base::TimeTicks::Now()); |
+ } |
+ } else if (type == content::NOTIFICATION_RENDERER_PROCESS_CLOSED) { |
+ base::TerminationStatus status = |
+ content::Details<content::RenderProcessHost::RendererClosedDetails>( |
+ details)->status; |
+ switch (status) { |
+ case base::TERMINATION_STATUS_NORMAL_TERMINATION: |
+ break; |
+ |
+ case base::TERMINATION_STATUS_ABNORMAL_TERMINATION: |
+ case base::TERMINATION_STATUS_PROCESS_WAS_KILLED: |
+ case base::TERMINATION_STATUS_PROCESS_CRASHED: |
+ crashed_tab_count_++; |
+ break; |
+ |
+ case base::TERMINATION_STATUS_STILL_RUNNING: |
+ LOG(ERROR) << "Got RENDERER_PROCESS_CLOSED notification, " |
+ << "but the process is still running. We may miss further " |
+ << "crash notification, resulting in hangs."; |
+ break; |
+ |
+ default: |
+ LOG(ERROR) << "Unhandled termination status " << status; |
+ NOTREACHED(); |
+ break; |
+ } |
+ } else { |
+ NOTREACHED(); |
+ } |
+} |
+ |
+void InitialLoadObserver::GetTimingInformationForTab( |
+ int routing_id, |
+ TabLoadTime* result, |
+ base::TimeTicks* timer_start) const { |
+ TabTimeMap::const_iterator it = loading_tabs_.find(routing_id); |
+ if (it == loading_tabs_.end()) { |
+ *timer_start = base::TimeTicks(); |
+ *result = TabLoadTime(); |
+ return; |
+ } |
+ |
+ *timer_start = init_time_; |
+ *result = it->second; |
+} |
+ |
+StatsCollectionMessageFilter::StatsCollectionMessageFilter() { |
+ // StatsCollectionMessageFilter's lifetime is tied to a renderer, we want |
+ // InitialLoadObserver to stick around and listen for tab loads indefinitely. |
+ InitialLoadObserver::GetInstance(); |
+} |
+ |
+void StatsCollectionMessageFilter::OnChannelConnected(int32 peer_pid) { |
+ BrowserMessageFilter::OnChannelConnected(peer_pid); |
+} |
+ |
+bool StatsCollectionMessageFilter::OnMessageReceived( |
+ const IPC::Message& message, |
+ bool* message_was_ok) { |
+ bool handled = true; |
+ IPC_BEGIN_MESSAGE_MAP_EX(StatsCollectionMessageFilter, message, |
+ *message_was_ok) |
+ IPC_MESSAGE_HANDLER(ChildProcessHostMsg_GetTabLoadTimingInformation, |
+ OnGetTabLoadTimingInformation) |
+ IPC_MESSAGE_UNHANDLED(handled = false) |
+ IPC_END_MESSAGE_MAP_EX() |
+ return handled; |
+} |
+ |
+StatsCollectionMessageFilter::~StatsCollectionMessageFilter() { |
+} |
+ |
+void StatsCollectionMessageFilter::OnGetTabLoadTimingInformation( |
+ int routing_id, |
+ TabLoadTime* tab_timing_data, |
+ base::TimeTicks* timer_start) { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); |
+ InitialLoadObserver::GetInstance()->GetTimingInformationForTab( |
+ routing_id, tab_timing_data, timer_start); |
+} |
+ |
+} // namespace content |