/* * Copyright 2009, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // Helper class to manage the process of uploading metrics. #include #include #include #include #include "base/logging.h" #include "statsreport/uploader.h" #include "statsreport/aggregator-win32.h" #include "statsreport/const-win32.h" #include "statsreport/persistent_iterator-win32.h" #include "statsreport/formatter.h" #include "statsreport/common/const_product.h" namespace stats_report { // TODO: Refactor to avoid cross platform code duplication. bool AggregateMetrics() { using stats_report::MetricsAggregatorWin32; MetricsAggregatorWin32 aggregator(g_global_metrics, PRODUCT_NAME_STRING_WIDE); if (!aggregator.AggregateMetrics()) { DLOG(WARNING) << "Metrics aggregation failed for reasons unknown"; return false; } return true; } static bool ReportMetrics(const char* extra_url_data, const char* user_agent, DWORD interval, StatsUploader* stats_uploader) { PersistentMetricsIteratorWin32 it(PRODUCT_NAME_STRING_WIDE), end; Formatter formatter(CStringA(PRODUCT_NAME_STRING), interval); for (; it != end; ++it) formatter.AddMetric(*it); DLOG(INFO) << "formatter.output() = " << formatter.output(); return stats_uploader->UploadMetrics(extra_url_data, user_agent, formatter.output().c_str()); } void ResetPersistentMetrics(CRegKey *key) { key->DeleteValue(kLastTransmissionTimeValueName); key->DeleteSubKey(kCountsKeyName); key->DeleteSubKey(kTimingsKeyName); key->DeleteSubKey(kIntegersKeyName); key->DeleteSubKey(kBooleansKeyName); } // Returns: // true if metrics were uploaded successfully, false otherwise // Note: False does not necessarily mean an error, just that no metrics // were uploaded bool AggregateAndReportMetrics(const char* extra_url_arguments, const char* user_agent, bool force_report, bool save_old_metrics) { StatsUploader stats_uploader; return TestableAggregateAndReportMetrics(extra_url_arguments, user_agent, force_report, save_old_metrics, &stats_uploader); } // Returns: // true if metrics were uploaded successfully, false otherwise // Note: False does not necessarily mean an error, just that no metrics // were uploaded bool TestableAggregateAndReportMetrics(const char* extra_url_arguments, const char* user_agent, bool force_report, bool save_old_metrics, StatsUploader* stats_uploader) { CString key_name; key_name.Format(kStatsKeyFormatString, PRODUCT_NAME_STRING_WIDE); CRegKey key; LONG err = key.Create(HKEY_CURRENT_USER, key_name); if (ERROR_SUCCESS != err) { DLOG(WARNING) << "Unable to open metrics key"; return false; } DWORD now = static_cast(time(NULL)); // Retrieve the last transmission time DWORD last_transmission_time; DWORD value_type; ULONG value_len = sizeof(last_transmission_time); err = key.QueryValue(kLastTransmissionTimeValueName, &value_type, &last_transmission_time, &value_len); // if last transmission time is missing or at all hinky, then // let's wipe all info and start afresh. if (ERROR_SUCCESS != err || REG_DWORD != value_type || sizeof(last_transmission_time) != value_len || last_transmission_time > now) { DLOG(WARNING) << "Hinky or missing last transmission time, wiping stats"; if (!save_old_metrics) ResetPersistentMetrics(&key); err = key.SetValue(kLastTransmissionTimeValueName, REG_DWORD, &now, sizeof(now)); if (ERROR_SUCCESS != err) { DLOG(ERROR) << "Unable to write last transmission value, error " << std::hex << err; } // Force a report of the stats so we get everything currently in there. force_report = true; // we just wiped everything, let's not waste any more time // return; <-- skipping this since we still want to aggregate } if (!AggregateMetrics()) { DLOG(INFO) << "AggregateMetrics returned false"; return false; } DLOG(INFO) << "Last transimission time: " << last_transmission_time; DLOG(INFO) << "Now: " << now; DLOG(INFO) << "Now - Last transmission time: " << now - last_transmission_time; DLOG(INFO) << "Compared to: " << kStatsUploadIntervalSec; // Set last_transmission_time such that it will force // an upload of the metrics if (force_report) { last_transmission_time = now - kStatsUploadIntervalSec; } if (now - last_transmission_time >= kStatsUploadIntervalSec) { bool report_result = ReportMetrics(extra_url_arguments, user_agent, now - last_transmission_time, stats_uploader); if (report_result) { DLOG(INFO) << "Stats upload successful, resetting metrics"; ResetPersistentMetrics(&key); } else { DLOG(WARNING) << "Stats upload failed"; } // No matter what, wait another upload interval to try again. It's better // to report older stats than hammer on the stats server exactly when it's // failed. err = key.SetValue(kLastTransmissionTimeValueName, REG_DWORD, &now, sizeof(now)); return report_result; } return false; } // Used primarily for testing. Default functionality. bool StatsUploader::UploadMetrics(const char* extra_url_data, const char* user_agent, const char *content) { return stats_report::UploadMetrics(extra_url_data, user_agent, content); } } // namespace stats_report