OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "chrome/browser/metrics/metrics_log.h" | 5 #include "chrome/browser/metrics/metrics_log.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "base/basictypes.h" | 10 #include "base/basictypes.h" |
11 #include "base/file_util.h" | 11 #include "base/file_util.h" |
12 #include "base/lazy_instance.h" | 12 #include "base/lazy_instance.h" |
13 #include "base/memory/scoped_ptr.h" | 13 #include "base/memory/scoped_ptr.h" |
14 #include "base/perftimer.h" | 14 #include "base/perftimer.h" |
| 15 #include "base/string_number_conversions.h" |
15 #include "base/string_util.h" | 16 #include "base/string_util.h" |
16 #include "base/sys_info.h" | 17 #include "base/sys_info.h" |
17 #include "base/third_party/nspr/prtime.h" | 18 #include "base/third_party/nspr/prtime.h" |
18 #include "base/time.h" | 19 #include "base/time.h" |
19 #include "base/utf_string_conversions.h" | 20 #include "base/utf_string_conversions.h" |
20 #include "chrome/browser/autocomplete/autocomplete.h" | 21 #include "chrome/browser/autocomplete/autocomplete.h" |
21 #include "chrome/browser/autocomplete/autocomplete_match.h" | 22 #include "chrome/browser/autocomplete/autocomplete_match.h" |
22 #include "chrome/browser/browser_process.h" | 23 #include "chrome/browser/browser_process.h" |
| 24 #include "chrome/browser/gpu_performance_stats.h" |
23 #include "chrome/browser/plugin_prefs.h" | 25 #include "chrome/browser/plugin_prefs.h" |
24 #include "chrome/browser/prefs/pref_service.h" | 26 #include "chrome/browser/prefs/pref_service.h" |
25 #include "chrome/browser/profiles/profile_manager.h" | 27 #include "chrome/browser/profiles/profile_manager.h" |
26 #include "chrome/common/chrome_version_info.h" | 28 #include "chrome/common/chrome_version_info.h" |
27 #include "chrome/common/logging_chrome.h" | 29 #include "chrome/common/logging_chrome.h" |
| 30 #include "chrome/common/metrics/proto/omnibox_event.pb.h" |
| 31 #include "chrome/common/metrics/proto/system_profile.pb.h" |
28 #include "chrome/common/pref_names.h" | 32 #include "chrome/common/pref_names.h" |
| 33 #include "content/public/browser/content_browser_client.h" |
29 #include "content/public/browser/gpu_data_manager.h" | 34 #include "content/public/browser/gpu_data_manager.h" |
30 #include "content/public/common/gpu_info.h" | 35 #include "content/public/common/gpu_info.h" |
| 36 #include "content/public/common/content_client.h" |
31 #include "googleurl/src/gurl.h" | 37 #include "googleurl/src/gurl.h" |
32 #include "ui/gfx/screen.h" | 38 #include "ui/gfx/screen.h" |
33 #include "webkit/plugins/webplugininfo.h" | 39 #include "webkit/plugins/webplugininfo.h" |
34 | 40 |
35 #define OPEN_ELEMENT_FOR_SCOPE(name) ScopedElement scoped_element(this, name) | 41 #define OPEN_ELEMENT_FOR_SCOPE(name) ScopedElement scoped_element(this, name) |
36 | 42 |
37 // http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx | 43 // http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx |
38 #if defined(OS_WIN) | 44 #if defined(OS_WIN) |
39 extern "C" IMAGE_DOS_HEADER __ImageBase; | 45 extern "C" IMAGE_DOS_HEADER __ImageBase; |
40 #endif | 46 #endif |
41 | 47 |
42 using content::GpuDataManager; | 48 using content::GpuDataManager; |
| 49 using metrics::OmniboxEventProto; |
| 50 using metrics::SystemProfileProto; |
43 | 51 |
44 namespace { | 52 namespace { |
45 | 53 |
46 // Returns the date at which the current metrics client ID was created as | 54 // Returns the date at which the current metrics client ID was created as |
47 // a string containing milliseconds since the epoch, or "0" if none was found. | 55 // a string containing milliseconds since the epoch, or "0" if none was found. |
48 std::string GetInstallDate() { | 56 std::string GetInstallDate() { |
49 PrefService* pref = g_browser_process->local_state(); | 57 PrefService* pref = g_browser_process->local_state(); |
50 if (pref) { | 58 if (pref) { |
51 return pref->GetString(prefs::kMetricsClientIDTimestamp); | 59 return pref->GetString(prefs::kMetricsClientIDTimestamp); |
52 } else { | 60 } else { |
53 NOTREACHED(); | 61 NOTREACHED(); |
54 return "0"; | 62 return "0"; |
55 } | 63 } |
56 } | 64 } |
57 | 65 |
| 66 OmniboxEventProto::InputType AsOmniboxEventInputType( |
| 67 AutocompleteInput::Type type) { |
| 68 switch (type) { |
| 69 case AutocompleteInput::INVALID: |
| 70 return OmniboxEventProto::INVALID; |
| 71 case AutocompleteInput::UNKNOWN: |
| 72 return OmniboxEventProto::UNKNOWN; |
| 73 case AutocompleteInput::REQUESTED_URL: |
| 74 return OmniboxEventProto::REQUESTED_URL; |
| 75 case AutocompleteInput::URL: |
| 76 return OmniboxEventProto::URL; |
| 77 case AutocompleteInput::QUERY: |
| 78 return OmniboxEventProto::QUERY; |
| 79 case AutocompleteInput::FORCED_QUERY: |
| 80 return OmniboxEventProto::FORCED_QUERY; |
| 81 default: |
| 82 NOTREACHED(); |
| 83 return OmniboxEventProto::INVALID; |
| 84 } |
| 85 } |
| 86 |
| 87 OmniboxEventProto::Suggestion::ProviderType AsOmniboxEventProviderType( |
| 88 const AutocompleteProvider* provider) { |
| 89 if (!provider) |
| 90 return OmniboxEventProto::Suggestion::UNKNOWN_PROVIDER; |
| 91 |
| 92 const std::string& name = provider->name(); |
| 93 if (name == "HistoryURL") |
| 94 return OmniboxEventProto::Suggestion::URL; |
| 95 if (name == "HistoryContents") |
| 96 return OmniboxEventProto::Suggestion::HISTORY_CONTENTS; |
| 97 if (name == "HistoryQuickProvider") |
| 98 return OmniboxEventProto::Suggestion::HISTORY_QUICK; |
| 99 if (name == "Search") |
| 100 return OmniboxEventProto::Suggestion::SEARCH; |
| 101 if (name == "Keyword") |
| 102 return OmniboxEventProto::Suggestion::KEYWORD; |
| 103 if (name == "Builtin") |
| 104 return OmniboxEventProto::Suggestion::BUILTIN; |
| 105 if (name == "ShortcutsProvider") |
| 106 return OmniboxEventProto::Suggestion::SHORTCUTS; |
| 107 if (name == "ExtensionApps") |
| 108 return OmniboxEventProto::Suggestion::EXTENSION_APPS; |
| 109 |
| 110 NOTREACHED(); |
| 111 return OmniboxEventProto::Suggestion::UNKNOWN_PROVIDER; |
| 112 } |
| 113 |
| 114 OmniboxEventProto::Suggestion::ResultType AsOmniboxEventResultType( |
| 115 AutocompleteMatch::Type type) { |
| 116 switch (type) { |
| 117 case AutocompleteMatch::URL_WHAT_YOU_TYPED: |
| 118 return OmniboxEventProto::Suggestion::URL_WHAT_YOU_TYPED; |
| 119 case AutocompleteMatch::HISTORY_URL: |
| 120 return OmniboxEventProto::Suggestion::HISTORY_URL; |
| 121 case AutocompleteMatch::HISTORY_TITLE: |
| 122 return OmniboxEventProto::Suggestion::HISTORY_TITLE; |
| 123 case AutocompleteMatch::HISTORY_BODY: |
| 124 return OmniboxEventProto::Suggestion::HISTORY_BODY; |
| 125 case AutocompleteMatch::HISTORY_KEYWORD: |
| 126 return OmniboxEventProto::Suggestion::HISTORY_KEYWORD; |
| 127 case AutocompleteMatch::NAVSUGGEST: |
| 128 return OmniboxEventProto::Suggestion::NAVSUGGEST; |
| 129 case AutocompleteMatch::SEARCH_WHAT_YOU_TYPED: |
| 130 return OmniboxEventProto::Suggestion::SEARCH_WHAT_YOU_TYPED; |
| 131 case AutocompleteMatch::SEARCH_HISTORY: |
| 132 return OmniboxEventProto::Suggestion::SEARCH_HISTORY; |
| 133 case AutocompleteMatch::SEARCH_SUGGEST: |
| 134 return OmniboxEventProto::Suggestion::SEARCH_SUGGEST; |
| 135 case AutocompleteMatch::SEARCH_OTHER_ENGINE: |
| 136 return OmniboxEventProto::Suggestion::SEARCH_OTHER_ENGINE; |
| 137 case AutocompleteMatch::EXTENSION_APP: |
| 138 return OmniboxEventProto::Suggestion::EXTENSION_APP; |
| 139 default: |
| 140 NOTREACHED(); |
| 141 return OmniboxEventProto::Suggestion::UNKNOWN_RESULT_TYPE; |
| 142 } |
| 143 } |
| 144 |
58 // Returns the plugin preferences corresponding for this user, if available. | 145 // Returns the plugin preferences corresponding for this user, if available. |
59 // If multiple user profiles are loaded, returns the preferences corresponding | 146 // If multiple user profiles are loaded, returns the preferences corresponding |
60 // to an arbitrary one of the profiles. | 147 // to an arbitrary one of the profiles. |
61 PluginPrefs* GetPluginPrefs() { | 148 PluginPrefs* GetPluginPrefs() { |
62 ProfileManager* profile_manager = g_browser_process->profile_manager(); | 149 ProfileManager* profile_manager = g_browser_process->profile_manager(); |
| 150 |
| 151 if (!profile_manager) { |
| 152 // The profile manager can be NULL when testing. |
| 153 return NULL; |
| 154 } |
| 155 |
63 std::vector<Profile*> profiles = profile_manager->GetLoadedProfiles(); | 156 std::vector<Profile*> profiles = profile_manager->GetLoadedProfiles(); |
64 if (profiles.empty()) | 157 if (profiles.empty()) |
65 return NULL; | 158 return NULL; |
66 | 159 |
67 return PluginPrefs::GetForProfile(profiles.front()); | 160 return PluginPrefs::GetForProfile(profiles.front()); |
68 } | 161 } |
69 | 162 |
| 163 // Fills |plugin| with the info contained in |plugin_info| and |plugin_prefs|. |
| 164 void SetPluginInfo(const webkit::WebPluginInfo& plugin_info, |
| 165 const PluginPrefs* plugin_prefs, |
| 166 SystemProfileProto::Plugin* plugin) { |
| 167 plugin->set_name(UTF16ToUTF8(plugin_info.name)); |
| 168 plugin->set_filename(plugin_info.path.BaseName().AsUTF8Unsafe()); |
| 169 plugin->set_version(UTF16ToUTF8(plugin_info.version)); |
| 170 if (plugin_prefs) |
| 171 plugin->set_is_disabled(!plugin_prefs->IsPluginEnabled(plugin_info)); |
| 172 } |
| 173 |
70 } // namespace | 174 } // namespace |
71 | 175 |
72 static base::LazyInstance<std::string>::Leaky | 176 static base::LazyInstance<std::string>::Leaky |
73 g_version_extension = LAZY_INSTANCE_INITIALIZER; | 177 g_version_extension = LAZY_INSTANCE_INITIALIZER; |
74 | 178 |
75 MetricsLog::MetricsLog(const std::string& client_id, int session_id) | 179 MetricsLog::MetricsLog(const std::string& client_id, int session_id) |
76 : MetricsLogBase(client_id, session_id, MetricsLog::GetVersionString()) {} | 180 : MetricsLogBase(client_id, session_id, MetricsLog::GetVersionString()) {} |
77 | 181 |
78 MetricsLog::~MetricsLog() {} | 182 MetricsLog::~MetricsLog() {} |
79 | 183 |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
117 // static | 221 // static |
118 void MetricsLog::set_version_extension(const std::string& extension) { | 222 void MetricsLog::set_version_extension(const std::string& extension) { |
119 g_version_extension.Get() = extension; | 223 g_version_extension.Get() = extension; |
120 } | 224 } |
121 | 225 |
122 // static | 226 // static |
123 const std::string& MetricsLog::version_extension() { | 227 const std::string& MetricsLog::version_extension() { |
124 return g_version_extension.Get(); | 228 return g_version_extension.Get(); |
125 } | 229 } |
126 | 230 |
127 void MetricsLog::RecordIncrementalStabilityElements() { | 231 void MetricsLog::RecordIncrementalStabilityElements( |
| 232 const std::vector<webkit::WebPluginInfo>& plugin_list) { |
128 DCHECK(!locked_); | 233 DCHECK(!locked_); |
129 | 234 |
130 PrefService* pref = g_browser_process->local_state(); | 235 PrefService* pref = g_browser_process->local_state(); |
131 DCHECK(pref); | 236 DCHECK(pref); |
132 | 237 |
133 OPEN_ELEMENT_FOR_SCOPE("profile"); | 238 OPEN_ELEMENT_FOR_SCOPE("profile"); |
134 WriteCommonEventAttributes(); | 239 WriteCommonEventAttributes(); |
135 | 240 |
136 WriteInstallElement(); | 241 WriteInstallElement(); |
137 | 242 |
138 { | 243 { |
139 OPEN_ELEMENT_FOR_SCOPE("stability"); // Minimal set of stability elements. | 244 OPEN_ELEMENT_FOR_SCOPE("stability"); // Minimal set of stability elements. |
140 WriteRequiredStabilityAttributes(pref); | 245 WriteRequiredStabilityAttributes(pref); |
141 WriteRealtimeStabilityAttributes(pref); | 246 WriteRealtimeStabilityAttributes(pref); |
142 | 247 |
143 WritePluginStabilityElements(pref); | 248 WritePluginStabilityElements(plugin_list, pref); |
144 } | 249 } |
145 } | 250 } |
146 | 251 |
147 void MetricsLog::WriteStabilityElement(PrefService* pref) { | 252 void MetricsLog::WriteStabilityElement( |
| 253 const std::vector<webkit::WebPluginInfo>& plugin_list, |
| 254 PrefService* pref) { |
148 DCHECK(!locked_); | 255 DCHECK(!locked_); |
149 | 256 |
150 DCHECK(pref); | |
151 | |
152 // Get stability attributes out of Local State, zeroing out stored values. | 257 // Get stability attributes out of Local State, zeroing out stored values. |
153 // NOTE: This could lead to some data loss if this report isn't successfully | 258 // NOTE: This could lead to some data loss if this report isn't successfully |
154 // sent, but that's true for all the metrics. | 259 // sent, but that's true for all the metrics. |
155 | 260 |
156 OPEN_ELEMENT_FOR_SCOPE("stability"); | 261 OPEN_ELEMENT_FOR_SCOPE("stability"); |
157 WriteRequiredStabilityAttributes(pref); | 262 WriteRequiredStabilityAttributes(pref); |
158 WriteRealtimeStabilityAttributes(pref); | 263 WriteRealtimeStabilityAttributes(pref); |
159 | 264 |
| 265 int incomplete_shutdown_count = |
| 266 pref->GetInteger(prefs::kStabilityIncompleteSessionEndCount); |
| 267 pref->SetInteger(prefs::kStabilityIncompleteSessionEndCount, 0); |
| 268 int breakpad_registration_success_count = |
| 269 pref->GetInteger(prefs::kStabilityBreakpadRegistrationSuccess); |
| 270 pref->SetInteger(prefs::kStabilityBreakpadRegistrationSuccess, 0); |
| 271 int breakpad_registration_failure_count = |
| 272 pref->GetInteger(prefs::kStabilityBreakpadRegistrationFail); |
| 273 pref->SetInteger(prefs::kStabilityBreakpadRegistrationFail, 0); |
| 274 int debugger_present_count = |
| 275 pref->GetInteger(prefs::kStabilityDebuggerPresent); |
| 276 pref->SetInteger(prefs::kStabilityDebuggerPresent, 0); |
| 277 int debugger_not_present_count = |
| 278 pref->GetInteger(prefs::kStabilityDebuggerNotPresent); |
| 279 pref->SetInteger(prefs::kStabilityDebuggerNotPresent, 0); |
| 280 |
160 // TODO(jar): The following are all optional, so we *could* optimize them for | 281 // TODO(jar): The following are all optional, so we *could* optimize them for |
161 // values of zero (and not include them). | 282 // values of zero (and not include them). |
162 WriteIntAttribute("incompleteshutdowncount", | |
163 pref->GetInteger( | |
164 prefs::kStabilityIncompleteSessionEndCount)); | |
165 pref->SetInteger(prefs::kStabilityIncompleteSessionEndCount, 0); | |
166 | 283 |
| 284 // Write the XML version. |
| 285 WriteIntAttribute("incompleteshutdowncount", incomplete_shutdown_count); |
| 286 WriteIntAttribute("breakpadregistrationok", |
| 287 breakpad_registration_success_count); |
| 288 WriteIntAttribute("breakpadregistrationfail", |
| 289 breakpad_registration_failure_count); |
| 290 WriteIntAttribute("debuggerpresent", debugger_present_count); |
| 291 WriteIntAttribute("debuggernotpresent", debugger_not_present_count); |
167 | 292 |
168 WriteIntAttribute("breakpadregistrationok", | 293 // Write the protobuf version. |
169 pref->GetInteger(prefs::kStabilityBreakpadRegistrationSuccess)); | 294 SystemProfileProto::Stability* stability = |
170 pref->SetInteger(prefs::kStabilityBreakpadRegistrationSuccess, 0); | 295 uma_proto_.mutable_system_profile()->mutable_stability(); |
171 WriteIntAttribute("breakpadregistrationfail", | 296 stability->set_incomplete_shutdown_count(incomplete_shutdown_count); |
172 pref->GetInteger(prefs::kStabilityBreakpadRegistrationFail)); | 297 stability->set_breakpad_registration_success_count( |
173 pref->SetInteger(prefs::kStabilityBreakpadRegistrationFail, 0); | 298 breakpad_registration_success_count); |
174 WriteIntAttribute("debuggerpresent", | 299 stability->set_breakpad_registration_failure_count( |
175 pref->GetInteger(prefs::kStabilityDebuggerPresent)); | 300 breakpad_registration_failure_count); |
176 pref->SetInteger(prefs::kStabilityDebuggerPresent, 0); | 301 stability->set_debugger_present_count(debugger_present_count); |
177 WriteIntAttribute("debuggernotpresent", | 302 stability->set_debugger_not_present_count(debugger_not_present_count); |
178 pref->GetInteger(prefs::kStabilityDebuggerNotPresent)); | |
179 pref->SetInteger(prefs::kStabilityDebuggerNotPresent, 0); | |
180 | 303 |
181 WritePluginStabilityElements(pref); | 304 WritePluginStabilityElements(plugin_list, pref); |
182 } | 305 } |
183 | 306 |
184 void MetricsLog::WritePluginStabilityElements(PrefService* pref) { | 307 void MetricsLog::WritePluginStabilityElements( |
| 308 const std::vector<webkit::WebPluginInfo>& plugin_list, |
| 309 PrefService* pref) { |
185 // Now log plugin stability info. | 310 // Now log plugin stability info. |
186 const ListValue* plugin_stats_list = pref->GetList( | 311 const ListValue* plugin_stats_list = pref->GetList( |
187 prefs::kStabilityPluginStats); | 312 prefs::kStabilityPluginStats); |
188 if (!plugin_stats_list) | 313 if (!plugin_stats_list) |
189 return; | 314 return; |
190 | 315 |
191 OPEN_ELEMENT_FOR_SCOPE("plugins"); | 316 OPEN_ELEMENT_FOR_SCOPE("plugins"); |
| 317 SystemProfileProto::Stability* stability = |
| 318 uma_proto_.mutable_system_profile()->mutable_stability(); |
| 319 PluginPrefs* plugin_prefs = GetPluginPrefs(); |
192 for (ListValue::const_iterator iter = plugin_stats_list->begin(); | 320 for (ListValue::const_iterator iter = plugin_stats_list->begin(); |
193 iter != plugin_stats_list->end(); ++iter) { | 321 iter != plugin_stats_list->end(); ++iter) { |
194 if (!(*iter)->IsType(Value::TYPE_DICTIONARY)) { | 322 if (!(*iter)->IsType(Value::TYPE_DICTIONARY)) { |
195 NOTREACHED(); | 323 NOTREACHED(); |
196 continue; | 324 continue; |
197 } | 325 } |
198 DictionaryValue* plugin_dict = static_cast<DictionaryValue*>(*iter); | 326 DictionaryValue* plugin_dict = static_cast<DictionaryValue*>(*iter); |
199 | 327 |
200 std::string plugin_name; | 328 std::string plugin_name; |
201 plugin_dict->GetString(prefs::kStabilityPluginName, &plugin_name); | 329 plugin_dict->GetString(prefs::kStabilityPluginName, &plugin_name); |
202 | 330 |
| 331 std::string base64_name_hash; |
| 332 uint64 numeric_name_hash_ignored; |
| 333 CreateHashes(plugin_name, &base64_name_hash, &numeric_name_hash_ignored); |
| 334 |
| 335 // Write the XML verison. |
203 OPEN_ELEMENT_FOR_SCOPE("pluginstability"); | 336 OPEN_ELEMENT_FOR_SCOPE("pluginstability"); |
204 // Use "filename" instead of "name", otherwise we need to update the | 337 // Use "filename" instead of "name", otherwise we need to update the |
205 // UMA servers. | 338 // UMA servers. |
206 WriteAttribute("filename", CreateBase64Hash(plugin_name)); | 339 WriteAttribute("filename", base64_name_hash); |
207 | 340 |
208 int launches = 0; | 341 int launches = 0; |
209 plugin_dict->GetInteger(prefs::kStabilityPluginLaunches, &launches); | 342 plugin_dict->GetInteger(prefs::kStabilityPluginLaunches, &launches); |
210 WriteIntAttribute("launchcount", launches); | 343 WriteIntAttribute("launchcount", launches); |
211 | 344 |
212 int instances = 0; | 345 int instances = 0; |
213 plugin_dict->GetInteger(prefs::kStabilityPluginInstances, &instances); | 346 plugin_dict->GetInteger(prefs::kStabilityPluginInstances, &instances); |
214 WriteIntAttribute("instancecount", instances); | 347 WriteIntAttribute("instancecount", instances); |
215 | 348 |
216 int crashes = 0; | 349 int crashes = 0; |
217 plugin_dict->GetInteger(prefs::kStabilityPluginCrashes, &crashes); | 350 plugin_dict->GetInteger(prefs::kStabilityPluginCrashes, &crashes); |
218 WriteIntAttribute("crashcount", crashes); | 351 WriteIntAttribute("crashcount", crashes); |
| 352 |
| 353 // Write the protobuf version. |
| 354 // Note that this search is potentially a quadratic operation, but given the |
| 355 // low number of plugins installed on a "reasonable" setup, this should be |
| 356 // fine. |
| 357 // TODO(isherman): Verify that this does not show up as a hotspot in |
| 358 // profiler runs. |
| 359 const webkit::WebPluginInfo* plugin_info = NULL; |
| 360 const string16 plugin_name_utf16 = UTF8ToUTF16(plugin_name); |
| 361 for (std::vector<webkit::WebPluginInfo>::const_iterator iter = |
| 362 plugin_list.begin(); |
| 363 iter != plugin_list.end(); ++iter) { |
| 364 if (iter->name == plugin_name_utf16) { |
| 365 plugin_info = &(*iter); |
| 366 break; |
| 367 } |
| 368 } |
| 369 |
| 370 if (!plugin_info) { |
| 371 NOTREACHED(); |
| 372 continue; |
| 373 } |
| 374 |
| 375 SystemProfileProto::Stability::PluginStability* plugin_stability = |
| 376 stability->add_plugin_stability(); |
| 377 SetPluginInfo(*plugin_info, plugin_prefs, |
| 378 plugin_stability->mutable_plugin()); |
| 379 plugin_stability->set_launch_count(launches); |
| 380 plugin_stability->set_instance_count(instances); |
| 381 plugin_stability->set_crash_count(crashes); |
219 } | 382 } |
220 | 383 |
221 pref->ClearPref(prefs::kStabilityPluginStats); | 384 pref->ClearPref(prefs::kStabilityPluginStats); |
222 } | 385 } |
223 | 386 |
| 387 // The server refuses data that doesn't have certain values. crashcount and |
| 388 // launchcount are currently "required" in the "stability" group. |
| 389 // TODO(isherman): Stop writing these attributes specially once the migration to |
| 390 // protobufs is complete. |
224 void MetricsLog::WriteRequiredStabilityAttributes(PrefService* pref) { | 391 void MetricsLog::WriteRequiredStabilityAttributes(PrefService* pref) { |
225 // The server refuses data that doesn't have certain values. crashcount and | 392 int launch_count = pref->GetInteger(prefs::kStabilityLaunchCount); |
226 // launchcount are currently "required" in the "stability" group. | |
227 WriteIntAttribute("launchcount", | |
228 pref->GetInteger(prefs::kStabilityLaunchCount)); | |
229 pref->SetInteger(prefs::kStabilityLaunchCount, 0); | 393 pref->SetInteger(prefs::kStabilityLaunchCount, 0); |
230 WriteIntAttribute("crashcount", | 394 int crash_count = pref->GetInteger(prefs::kStabilityCrashCount); |
231 pref->GetInteger(prefs::kStabilityCrashCount)); | |
232 pref->SetInteger(prefs::kStabilityCrashCount, 0); | 395 pref->SetInteger(prefs::kStabilityCrashCount, 0); |
| 396 |
| 397 // Write the XML version. |
| 398 WriteIntAttribute("launchcount", launch_count); |
| 399 WriteIntAttribute("crashcount", crash_count); |
| 400 |
| 401 // Write the protobuf version. |
| 402 SystemProfileProto::Stability* stability = |
| 403 uma_proto_.mutable_system_profile()->mutable_stability(); |
| 404 stability->set_launch_count(launch_count); |
| 405 stability->set_crash_count(crash_count); |
233 } | 406 } |
234 | 407 |
235 void MetricsLog::WriteRealtimeStabilityAttributes(PrefService* pref) { | 408 void MetricsLog::WriteRealtimeStabilityAttributes(PrefService* pref) { |
236 // Update the stats which are critical for real-time stability monitoring. | 409 // Update the stats which are critical for real-time stability monitoring. |
237 // Since these are "optional," only list ones that are non-zero, as the counts | 410 // Since these are "optional," only list ones that are non-zero, as the counts |
238 // are aggergated (summed) server side. | 411 // are aggergated (summed) server side. |
239 | 412 |
| 413 SystemProfileProto::Stability* stability = |
| 414 uma_proto_.mutable_system_profile()->mutable_stability(); |
240 int count = pref->GetInteger(prefs::kStabilityPageLoadCount); | 415 int count = pref->GetInteger(prefs::kStabilityPageLoadCount); |
241 if (count) { | 416 if (count) { |
242 WriteIntAttribute("pageloadcount", count); | 417 WriteIntAttribute("pageloadcount", count); |
| 418 stability->set_page_load_count(count); |
243 pref->SetInteger(prefs::kStabilityPageLoadCount, 0); | 419 pref->SetInteger(prefs::kStabilityPageLoadCount, 0); |
244 } | 420 } |
245 | 421 |
246 count = pref->GetInteger(prefs::kStabilityRendererCrashCount); | 422 count = pref->GetInteger(prefs::kStabilityRendererCrashCount); |
247 if (count) { | 423 if (count) { |
248 WriteIntAttribute("renderercrashcount", count); | 424 WriteIntAttribute("renderercrashcount", count); |
| 425 stability->set_renderer_crash_count(count); |
249 pref->SetInteger(prefs::kStabilityRendererCrashCount, 0); | 426 pref->SetInteger(prefs::kStabilityRendererCrashCount, 0); |
250 } | 427 } |
251 | 428 |
252 count = pref->GetInteger(prefs::kStabilityExtensionRendererCrashCount); | 429 count = pref->GetInteger(prefs::kStabilityExtensionRendererCrashCount); |
253 if (count) { | 430 if (count) { |
254 WriteIntAttribute("extensionrenderercrashcount", count); | 431 WriteIntAttribute("extensionrenderercrashcount", count); |
| 432 stability->set_extension_renderer_crash_count(count); |
255 pref->SetInteger(prefs::kStabilityExtensionRendererCrashCount, 0); | 433 pref->SetInteger(prefs::kStabilityExtensionRendererCrashCount, 0); |
256 } | 434 } |
257 | 435 |
258 count = pref->GetInteger(prefs::kStabilityRendererHangCount); | 436 count = pref->GetInteger(prefs::kStabilityRendererHangCount); |
259 if (count) { | 437 if (count) { |
260 WriteIntAttribute("rendererhangcount", count); | 438 WriteIntAttribute("rendererhangcount", count); |
| 439 stability->set_renderer_hang_count(count); |
261 pref->SetInteger(prefs::kStabilityRendererHangCount, 0); | 440 pref->SetInteger(prefs::kStabilityRendererHangCount, 0); |
262 } | 441 } |
263 | 442 |
264 count = pref->GetInteger(prefs::kStabilityChildProcessCrashCount); | 443 count = pref->GetInteger(prefs::kStabilityChildProcessCrashCount); |
265 if (count) { | 444 if (count) { |
266 WriteIntAttribute("childprocesscrashcount", count); | 445 WriteIntAttribute("childprocesscrashcount", count); |
| 446 stability->set_child_process_crash_count(count); |
267 pref->SetInteger(prefs::kStabilityChildProcessCrashCount, 0); | 447 pref->SetInteger(prefs::kStabilityChildProcessCrashCount, 0); |
268 } | 448 } |
269 | 449 |
270 #if defined(OS_CHROMEOS) | 450 #if defined(OS_CHROMEOS) |
271 count = pref->GetInteger(prefs::kStabilityOtherUserCrashCount); | 451 count = pref->GetInteger(prefs::kStabilityOtherUserCrashCount); |
272 if (count) { | 452 if (count) { |
273 // TODO(kmixter): Write attribute once log server supports it | 453 stability->set_other_user_crash_count(count); |
274 // and remove warning log. | |
275 // WriteIntAttribute("otherusercrashcount", count); | |
276 LOG(WARNING) << "Not yet able to send otherusercrashcount=" | |
277 << count; | |
278 pref->SetInteger(prefs::kStabilityOtherUserCrashCount, 0); | 454 pref->SetInteger(prefs::kStabilityOtherUserCrashCount, 0); |
279 } | 455 } |
280 | 456 |
281 count = pref->GetInteger(prefs::kStabilityKernelCrashCount); | 457 count = pref->GetInteger(prefs::kStabilityKernelCrashCount); |
282 if (count) { | 458 if (count) { |
283 // TODO(kmixter): Write attribute once log server supports it | 459 stability->set_kernel_crash_count(count); |
284 // and remove warning log. | |
285 // WriteIntAttribute("kernelcrashcount", count); | |
286 LOG(WARNING) << "Not yet able to send kernelcrashcount=" | |
287 << count; | |
288 pref->SetInteger(prefs::kStabilityKernelCrashCount, 0); | 460 pref->SetInteger(prefs::kStabilityKernelCrashCount, 0); |
289 } | 461 } |
290 | 462 |
291 count = pref->GetInteger(prefs::kStabilitySystemUncleanShutdownCount); | 463 count = pref->GetInteger(prefs::kStabilitySystemUncleanShutdownCount); |
292 if (count) { | 464 if (count) { |
293 // TODO(kmixter): Write attribute once log server supports it | 465 stability->set_unclean_system_shutdown_count(count); |
294 // and remove warning log. | |
295 // WriteIntAttribute("systemuncleanshutdowns", count); | |
296 LOG(WARNING) << "Not yet able to send systemuncleanshutdowns=" | |
297 << count; | |
298 pref->SetInteger(prefs::kStabilitySystemUncleanShutdownCount, 0); | 466 pref->SetInteger(prefs::kStabilitySystemUncleanShutdownCount, 0); |
299 } | 467 } |
300 #endif // OS_CHROMEOS | 468 #endif // OS_CHROMEOS |
301 | 469 |
302 int64 recent_duration = GetIncrementalUptime(pref); | 470 int64 recent_duration = GetIncrementalUptime(pref); |
303 if (recent_duration) | 471 if (recent_duration) { |
304 WriteInt64Attribute("uptimesec", recent_duration); | 472 WriteInt64Attribute("uptimesec", recent_duration); |
| 473 stability->set_uptime_sec(recent_duration); |
| 474 } |
305 } | 475 } |
306 | 476 |
307 void MetricsLog::WritePluginList( | 477 void MetricsLog::WritePluginList( |
308 const std::vector<webkit::WebPluginInfo>& plugin_list) { | 478 const std::vector<webkit::WebPluginInfo>& plugin_list) { |
309 DCHECK(!locked_); | 479 DCHECK(!locked_); |
310 | 480 |
311 PluginPrefs* plugin_prefs = GetPluginPrefs(); | 481 PluginPrefs* plugin_prefs = GetPluginPrefs(); |
312 | 482 |
313 OPEN_ELEMENT_FOR_SCOPE("plugins"); | 483 OPEN_ELEMENT_FOR_SCOPE("plugins"); |
314 | 484 SystemProfileProto* system_profile = uma_proto_.mutable_system_profile(); |
315 for (std::vector<webkit::WebPluginInfo>::const_iterator iter = | 485 for (std::vector<webkit::WebPluginInfo>::const_iterator iter = |
316 plugin_list.begin(); | 486 plugin_list.begin(); |
317 iter != plugin_list.end(); ++iter) { | 487 iter != plugin_list.end(); ++iter) { |
| 488 std::string base64_name_hash; |
| 489 uint64 numeric_name_hash_ignored; |
| 490 CreateHashes(UTF16ToUTF8(iter->name), |
| 491 &base64_name_hash, |
| 492 &numeric_name_hash_ignored); |
| 493 |
| 494 std::string filename_bytes = iter->path.BaseName().AsUTF8Unsafe(); |
| 495 std::string base64_filename_hash; |
| 496 uint64 numeric_filename_hash; |
| 497 CreateHashes(filename_bytes, |
| 498 &base64_filename_hash, |
| 499 &numeric_filename_hash); |
| 500 |
| 501 // Write the XML version. |
318 OPEN_ELEMENT_FOR_SCOPE("plugin"); | 502 OPEN_ELEMENT_FOR_SCOPE("plugin"); |
319 | 503 |
320 // Plugin name and filename are hashed for the privacy of those | 504 // Plugin name and filename are hashed for the privacy of those |
321 // testing unreleased new extensions. | 505 // testing unreleased new extensions. |
322 WriteAttribute("name", CreateBase64Hash(UTF16ToUTF8(iter->name))); | 506 WriteAttribute("name", base64_name_hash); |
323 std::string filename_bytes = | 507 WriteAttribute("filename", base64_filename_hash); |
324 #if defined(OS_WIN) | |
325 UTF16ToUTF8(iter->path.BaseName().value()); | |
326 #else | |
327 iter->path.BaseName().value(); | |
328 #endif | |
329 WriteAttribute("filename", CreateBase64Hash(filename_bytes)); | |
330 WriteAttribute("version", UTF16ToUTF8(iter->version)); | 508 WriteAttribute("version", UTF16ToUTF8(iter->version)); |
331 if (plugin_prefs) | 509 if (plugin_prefs) |
332 WriteIntAttribute("disabled", !plugin_prefs->IsPluginEnabled(*iter)); | 510 WriteIntAttribute("disabled", !plugin_prefs->IsPluginEnabled(*iter)); |
| 511 |
| 512 // Write the protobuf version. |
| 513 SystemProfileProto::Plugin* plugin = system_profile->add_plugin(); |
| 514 SetPluginInfo(*iter, plugin_prefs, plugin); |
333 } | 515 } |
334 } | 516 } |
335 | 517 |
336 void MetricsLog::WriteInstallElement() { | 518 void MetricsLog::WriteInstallElement() { |
| 519 std::string install_date = GetInstallDate(); |
| 520 |
| 521 // Write the XML version. |
337 OPEN_ELEMENT_FOR_SCOPE("install"); | 522 OPEN_ELEMENT_FOR_SCOPE("install"); |
338 WriteAttribute("installdate", GetInstallDate()); | 523 WriteAttribute("installdate", install_date); |
339 WriteIntAttribute("buildid", 0); // We're using appversion instead. | 524 WriteIntAttribute("buildid", 0); // We're using appversion instead. |
| 525 |
| 526 // Write the protobuf version. |
| 527 int numeric_install_date; |
| 528 bool success = base::StringToInt(install_date, &numeric_install_date); |
| 529 DCHECK(success); |
| 530 uma_proto_.mutable_system_profile()->set_install_date(numeric_install_date); |
340 } | 531 } |
341 | 532 |
342 void MetricsLog::RecordEnvironment( | 533 void MetricsLog::RecordEnvironment( |
343 const std::vector<webkit::WebPluginInfo>& plugin_list, | 534 const std::vector<webkit::WebPluginInfo>& plugin_list, |
344 const DictionaryValue* profile_metrics) { | 535 const DictionaryValue* profile_metrics) { |
345 DCHECK(!locked_); | 536 DCHECK(!locked_); |
346 | 537 |
347 PrefService* pref = g_browser_process->local_state(); | 538 PrefService* pref = g_browser_process->local_state(); |
348 | 539 |
349 OPEN_ELEMENT_FOR_SCOPE("profile"); | 540 OPEN_ELEMENT_FOR_SCOPE("profile"); |
350 WriteCommonEventAttributes(); | 541 WriteCommonEventAttributes(); |
351 | 542 |
352 WriteInstallElement(); | 543 WriteInstallElement(); |
353 | 544 |
354 WritePluginList(plugin_list); | 545 WritePluginList(plugin_list); |
355 | 546 |
356 WriteStabilityElement(pref); | 547 WriteStabilityElement(plugin_list, pref); |
357 | 548 |
| 549 SystemProfileProto* system_profile = uma_proto_.mutable_system_profile(); |
| 550 system_profile->set_application_locale( |
| 551 content::GetContentClient()->browser()->GetApplicationLocale()); |
| 552 |
| 553 SystemProfileProto::Hardware* hardware = system_profile->mutable_hardware(); |
358 { | 554 { |
| 555 std::string cpu_architecture = base::SysInfo::CPUArchitecture(); |
| 556 |
| 557 // Write the XML version. |
359 OPEN_ELEMENT_FOR_SCOPE("cpu"); | 558 OPEN_ELEMENT_FOR_SCOPE("cpu"); |
360 WriteAttribute("arch", base::SysInfo::CPUArchitecture()); | 559 WriteAttribute("arch", cpu_architecture); |
| 560 |
| 561 // Write the protobuf version. |
| 562 hardware->set_cpu_architecture(cpu_architecture); |
361 } | 563 } |
362 | 564 |
363 { | 565 { |
| 566 int system_memory_mb = base::SysInfo::AmountOfPhysicalMemoryMB(); |
| 567 |
| 568 // Write the XML version. |
364 OPEN_ELEMENT_FOR_SCOPE("memory"); | 569 OPEN_ELEMENT_FOR_SCOPE("memory"); |
365 WriteIntAttribute("mb", base::SysInfo::AmountOfPhysicalMemoryMB()); | 570 WriteIntAttribute("mb", system_memory_mb); |
366 #if defined(OS_WIN) | 571 #if defined(OS_WIN) |
367 WriteIntAttribute("dllbase", reinterpret_cast<int>(&__ImageBase)); | 572 WriteIntAttribute("dllbase", reinterpret_cast<int>(&__ImageBase)); |
368 #endif | 573 #endif |
| 574 |
| 575 // Write the protobuf version. |
| 576 hardware->set_system_ram_mb(system_memory_mb); |
| 577 #if defined(OS_WIN) |
| 578 hardware->set_dll_base(reinterpret_cast<uint64>(&__ImageBase)); |
| 579 #endif |
369 } | 580 } |
370 | 581 |
371 { | 582 { |
| 583 std::string os_name = base::SysInfo::OperatingSystemName(); |
| 584 std::string os_version = base::SysInfo::OperatingSystemVersion(); |
| 585 |
| 586 // Write the XML version. |
372 OPEN_ELEMENT_FOR_SCOPE("os"); | 587 OPEN_ELEMENT_FOR_SCOPE("os"); |
373 WriteAttribute("name", | 588 WriteAttribute("name", os_name); |
374 base::SysInfo::OperatingSystemName()); | 589 WriteAttribute("version", os_version); |
375 WriteAttribute("version", | 590 |
376 base::SysInfo::OperatingSystemVersion()); | 591 // Write the protobuf version. |
| 592 SystemProfileProto::OS* os = system_profile->mutable_os(); |
| 593 os->set_name(os_name); |
| 594 os->set_version(os_version); |
377 } | 595 } |
378 | 596 |
379 { | 597 { |
380 OPEN_ELEMENT_FOR_SCOPE("gpu"); | 598 OPEN_ELEMENT_FOR_SCOPE("gpu"); |
381 content::GPUInfo gpu_info = GpuDataManager::GetInstance()->GetGPUInfo(); | 599 const content::GPUInfo& gpu_info = |
| 600 GpuDataManager::GetInstance()->GetGPUInfo(); |
| 601 GpuPerformanceStats gpu_performance_stats = |
| 602 GpuPerformanceStats::RetrieveGpuPerformanceStats(); |
| 603 |
| 604 // Write the XML version. |
382 WriteIntAttribute("vendorid", gpu_info.vendor_id); | 605 WriteIntAttribute("vendorid", gpu_info.vendor_id); |
383 WriteIntAttribute("deviceid", gpu_info.device_id); | 606 WriteIntAttribute("deviceid", gpu_info.device_id); |
| 607 |
| 608 // Write the protobuf version. |
| 609 SystemProfileProto::Hardware::Graphics* gpu = hardware->mutable_gpu(); |
| 610 gpu->set_vendor_id(gpu_info.vendor_id); |
| 611 gpu->set_device_id(gpu_info.device_id); |
| 612 gpu->set_driver_version(gpu_info.driver_version); |
| 613 gpu->set_driver_date(gpu_info.driver_date); |
| 614 SystemProfileProto::Hardware::Graphics::PerformanceStatistics* |
| 615 gpu_performance = gpu->mutable_performance_statistics(); |
| 616 gpu_performance->set_graphics_score(gpu_performance_stats.graphics); |
| 617 gpu_performance->set_gaming_score(gpu_performance_stats.gaming); |
| 618 gpu_performance->set_overall_score(gpu_performance_stats.overall); |
384 } | 619 } |
385 | 620 |
386 { | 621 { |
| 622 const gfx::Size display_size = gfx::Screen::GetPrimaryMonitorSize(); |
| 623 int display_width = display_size.width(); |
| 624 int display_height = display_size.height(); |
| 625 int screen_count = gfx::Screen::GetNumMonitors(); |
| 626 |
| 627 // Write the XML version. |
387 OPEN_ELEMENT_FOR_SCOPE("display"); | 628 OPEN_ELEMENT_FOR_SCOPE("display"); |
388 const gfx::Size display_size = gfx::Screen::GetPrimaryMonitorSize(); | 629 WriteIntAttribute("xsize", display_width); |
389 WriteIntAttribute("xsize", display_size.width()); | 630 WriteIntAttribute("ysize", display_height); |
390 WriteIntAttribute("ysize", display_size.height()); | 631 WriteIntAttribute("screens", screen_count); |
391 WriteIntAttribute("screens", gfx::Screen::GetNumMonitors()); | 632 |
| 633 // Write the protobuf version. |
| 634 hardware->set_primary_screen_width(display_width); |
| 635 hardware->set_primary_screen_height(display_height); |
| 636 hardware->set_screen_count(screen_count); |
392 } | 637 } |
393 | 638 |
394 { | 639 { |
395 OPEN_ELEMENT_FOR_SCOPE("bookmarks"); | 640 OPEN_ELEMENT_FOR_SCOPE("bookmarks"); |
396 int num_bookmarks_on_bookmark_bar = | 641 int num_bookmarks_on_bookmark_bar = |
397 pref->GetInteger(prefs::kNumBookmarksOnBookmarkBar); | 642 pref->GetInteger(prefs::kNumBookmarksOnBookmarkBar); |
398 int num_folders_on_bookmark_bar = | 643 int num_folders_on_bookmark_bar = |
399 pref->GetInteger(prefs::kNumFoldersOnBookmarkBar); | 644 pref->GetInteger(prefs::kNumFoldersOnBookmarkBar); |
400 int num_bookmarks_in_other_bookmarks_folder = | 645 int num_bookmarks_in_other_bookmarks_folder = |
401 pref->GetInteger(prefs::kNumBookmarksInOtherBookmarkFolder); | 646 pref->GetInteger(prefs::kNumBookmarksInOtherBookmarkFolder); |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
486 NOTREACHED(); | 731 NOTREACHED(); |
487 break; | 732 break; |
488 } | 733 } |
489 } | 734 } |
490 } | 735 } |
491 } | 736 } |
492 | 737 |
493 void MetricsLog::RecordOmniboxOpenedURL(const AutocompleteLog& log) { | 738 void MetricsLog::RecordOmniboxOpenedURL(const AutocompleteLog& log) { |
494 DCHECK(!locked_); | 739 DCHECK(!locked_); |
495 | 740 |
| 741 // Write the XML version. |
496 OPEN_ELEMENT_FOR_SCOPE("uielement"); | 742 OPEN_ELEMENT_FOR_SCOPE("uielement"); |
497 WriteAttribute("action", "autocomplete"); | 743 WriteAttribute("action", "autocomplete"); |
498 WriteAttribute("targetidhash", ""); | 744 WriteAttribute("targetidhash", ""); |
499 // TODO(kochi): Properly track windows. | 745 // TODO(kochi): Properly track windows. |
500 WriteIntAttribute("window", 0); | 746 WriteIntAttribute("window", 0); |
501 if (log.tab_id != -1) { | 747 if (log.tab_id != -1) { |
502 // If we know what tab the autocomplete URL was opened in, log it. | 748 // If we know what tab the autocomplete URL was opened in, log it. |
503 WriteIntAttribute("tab", static_cast<int>(log.tab_id)); | 749 WriteIntAttribute("tab", static_cast<int>(log.tab_id)); |
504 } | 750 } |
505 WriteCommonEventAttributes(); | 751 WriteCommonEventAttributes(); |
(...skipping 25 matching lines...) Expand all Loading... |
531 if (i->provider) | 777 if (i->provider) |
532 WriteAttribute("provider", i->provider->name()); | 778 WriteAttribute("provider", i->provider->name()); |
533 const std::string result_type(AutocompleteMatch::TypeToString(i->type)); | 779 const std::string result_type(AutocompleteMatch::TypeToString(i->type)); |
534 if (!result_type.empty()) | 780 if (!result_type.empty()) |
535 WriteAttribute("resulttype", result_type); | 781 WriteAttribute("resulttype", result_type); |
536 WriteIntAttribute("relevance", i->relevance); | 782 WriteIntAttribute("relevance", i->relevance); |
537 WriteIntAttribute("isstarred", i->starred ? 1 : 0); | 783 WriteIntAttribute("isstarred", i->starred ? 1 : 0); |
538 } | 784 } |
539 } | 785 } |
540 | 786 |
| 787 // Write the protobuf version. |
| 788 OmniboxEventProto* omnibox_event = uma_proto_.add_omnibox_event(); |
| 789 omnibox_event->set_time(MetricsLogBase::GetCurrentTime()); |
| 790 if (log.tab_id != -1) { |
| 791 // If we know what tab the autocomplete URL was opened in, log it. |
| 792 omnibox_event->set_tab_id(log.tab_id); |
| 793 } |
| 794 omnibox_event->set_typed_length(log.text.length()); |
| 795 omnibox_event->set_selected_index(log.selected_index); |
| 796 omnibox_event->set_completed_length(log.inline_autocompleted_length); |
| 797 omnibox_event->set_input_type(AsOmniboxEventInputType(log.input_type)); |
| 798 for (AutocompleteResult::const_iterator i(log.result.begin()); |
| 799 i != log.result.end(); ++i) { |
| 800 OmniboxEventProto::Suggestion* suggestion = omnibox_event->add_suggestion(); |
| 801 suggestion->set_provider(AsOmniboxEventProviderType(i->provider)); |
| 802 suggestion->set_result_type(AsOmniboxEventResultType(i->type)); |
| 803 suggestion->set_relevance(i->relevance); |
| 804 suggestion->set_is_starred(i->starred); |
| 805 } |
| 806 |
541 ++num_events_; | 807 ++num_events_; |
542 } | 808 } |
OLD | NEW |