Chromium Code Reviews| 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 |