1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// This file defines a set of user experience metrics data recorded by
6// the MetricsService.  This is the unit of data that is sent to the server.
7
8#ifndef CHROME_COMMON_METRICS_HELPERS_H_
9#define CHROME_COMMON_METRICS_HELPERS_H_
10#pragma once
11
12#include <map>
13#include <string>
14
15#include "base/basictypes.h"
16#include "base/memory/scoped_ptr.h"
17#include "base/metrics/histogram.h"
18#include "base/time.h"
19#include "content/common/page_transition_types.h"
20
21class GURL;
22class MetricsLog;
23
24// This class provides base functionality for logging metrics data.
25class MetricsLogBase {
26 public:
27  // Creates a new metrics log
28  // client_id is the identifier for this profile on this installation
29  // session_id is an integer that's incremented on each application launch
30  MetricsLogBase(const std::string& client_id, int session_id,
31                 const std::string& version_string);
32  virtual ~MetricsLogBase();
33
34  // Records a user-initiated action.
35  void RecordUserAction(const char* key);
36
37  enum WindowEventType {
38    WINDOW_CREATE = 0,
39    WINDOW_OPEN,
40    WINDOW_CLOSE,
41    WINDOW_DESTROY
42  };
43
44  void RecordWindowEvent(WindowEventType type, int window_id, int parent_id);
45
46  // Records a page load.
47  // window_id - the index of the tab in which the load took place
48  // url - which URL was loaded
49  // origin - what kind of action initiated the load
50  // load_time - how long it took to load the page
51  void RecordLoadEvent(int window_id,
52                       const GURL& url,
53                       PageTransition::Type origin,
54                       int session_index,
55                       base::TimeDelta load_time);
56
57  // Record any changes in a given histogram for transmission.
58  void RecordHistogramDelta(const base::Histogram& histogram,
59                            const base::Histogram::SampleSet& snapshot);
60
61  // Stop writing to this record and generate the encoded representation.
62  // None of the Record* methods can be called after this is called.
63  void CloseLog();
64
65  // These methods allow retrieval of the encoded representation of the
66  // record.  They can only be called after CloseLog() has been called.
67  // GetEncodedLog returns false if buffer_size is less than
68  // GetEncodedLogSize();
69  int GetEncodedLogSize();
70  bool GetEncodedLog(char* buffer, int buffer_size);
71  // Returns an empty string on failure.
72  std::string GetEncodedLogString();
73
74  // Returns the amount of time in seconds that this log has been in use.
75  int GetElapsedSeconds();
76
77  int num_events() { return num_events_; }
78
79  void set_hardware_class(const std::string& hardware_class) {
80    hardware_class_ = hardware_class;
81  }
82
83  // Creates an MD5 hash of the given value, and returns hash as a byte
84  // buffer encoded as a std::string.
85  static std::string CreateHash(const std::string& value);
86
87  // Return a base64-encoded MD5 hash of the given string.
88  static std::string CreateBase64Hash(const std::string& string);
89
90  // Get the GMT buildtime for the current binary, expressed in seconds since
91  // Januray 1, 1970 GMT.
92  // The value is used to identify when a new build is run, so that previous
93  // reliability stats, from other builds, can be abandoned.
94  static int64 GetBuildTime();
95
96  // Use |extension| in all uploaded appversions in addition to the standard
97  // version string.
98  static void set_version_extension(const std::string& extension) {
99    version_extension_ = extension;
100  }
101
102  virtual MetricsLog* AsMetricsLog();
103
104 protected:
105  class XmlWrapper;
106
107  // Returns a string containing the current time.
108  // Virtual so that it can be overridden for testing.
109  virtual std::string GetCurrentTimeString();
110  // Helper class that invokes StartElement from constructor, and EndElement
111  // from destructor.
112  //
113  // Use the macro OPEN_ELEMENT_FOR_SCOPE to help avoid usage problems.
114  class ScopedElement {
115   public:
116    ScopedElement(MetricsLogBase* log, const std::string& name) : log_(log) {
117      DCHECK(log);
118      log->StartElement(name.c_str());
119    }
120
121    ScopedElement(MetricsLogBase* log, const char* name) : log_(log) {
122      DCHECK(log);
123      log->StartElement(name);
124    }
125
126    ~ScopedElement() {
127      log_->EndElement();
128    }
129
130   private:
131     MetricsLogBase* log_;
132  };
133  friend class ScopedElement;
134
135  static const char* WindowEventTypeToString(WindowEventType type);
136
137  // Frees the resources allocated by the XML document writer: the
138  // main writer object as well as the XML tree structure, if
139  // applicable.
140  void FreeDocWriter();
141
142  // Convenience versions of xmlWriter functions
143  void StartElement(const char* name);
144  void EndElement();
145  void WriteAttribute(const std::string& name, const std::string& value);
146  void WriteIntAttribute(const std::string& name, int value);
147  void WriteInt64Attribute(const std::string& name, int64 value);
148
149  // Write the attributes that are common to every metrics event type.
150  void WriteCommonEventAttributes();
151
152  // An extension that is appended to the appversion in each log.
153  static std::string version_extension_;
154
155  base::Time start_time_;
156  base::Time end_time_;
157
158  std::string client_id_;
159  std::string session_id_;
160  std::string hardware_class_;
161
162  // locked_ is true when record has been packed up for sending, and should
163  // no longer be written to.  It is only used for sanity checking and is
164  // not a real lock.
165  bool locked_;
166
167  // Isolated to limit the dependency on the XML library for our consumers.
168  XmlWrapper* xml_wrapper_;
169
170  int num_events_;  // the number of events recorded in this log
171
172  DISALLOW_COPY_AND_ASSIGN(MetricsLogBase);
173};
174
175// HistogramSender handles the logistics of gathering up available histograms
176// for transmission (such as from renderer to browser, or from browser to UMA
177// upload).  It has several pure virtual functions that are replaced in
178// derived classes to allow the exact lower level transmission mechanism,
179// or error report mechanism, to be replaced.  Since histograms can sit in
180// memory for an extended period of time, and are vulnerable to memory
181// corruption, this class also validates as much rendundancy as it can before
182// calling for the marginal change (a.k.a., delta) in a histogram to be sent
183// onward.
184class HistogramSender {
185 protected:
186  HistogramSender();
187  virtual ~HistogramSender();
188
189  // Snapshot all histograms, and transmit the delta.
190  // The arguments allow a derived class to select only a subset for
191  // transmission, or to set a flag in each transmitted histogram.
192  void TransmitAllHistograms(base::Histogram::Flags flags_to_set,
193                             bool send_only_uma);
194
195  // Send the histograms onward, as defined in a derived class.
196  // This is only called with a delta, listing samples that have not previously
197  // been transmitted.
198  virtual void TransmitHistogramDelta(
199      const base::Histogram& histogram,
200      const base::Histogram::SampleSet& snapshot) = 0;
201
202  // Record various errors found during attempts to send histograms.
203  virtual void InconsistencyDetected(int problem) = 0;
204  virtual void UniqueInconsistencyDetected(int problem) = 0;
205  virtual void SnapshotProblemResolved(int amount) = 0;
206
207 private:
208  // Maintain a map of histogram names to the sample stats we've sent.
209  typedef std::map<std::string, base::Histogram::SampleSet> LoggedSampleMap;
210  // List of histograms names, and their encontered corruptions.
211  typedef std::map<std::string, int> ProblemMap;
212
213  // Snapshot this histogram, and transmit the delta.
214  void TransmitHistogram(const base::Histogram& histogram);
215
216  // For histograms, record what we've already transmitted (as a sample for each
217  // histogram) so that we can send only the delta with the next log.
218  LoggedSampleMap logged_samples_;
219
220  // List of histograms found corrupt to be corrupt, and their problems.
221  scoped_ptr<ProblemMap> inconsistencies_;
222
223  DISALLOW_COPY_AND_ASSIGN(HistogramSender);
224};
225
226// This class provides base functionality for logging metrics data.
227// TODO(ananta)
228// Factor out more common code from chrome and chrome frame metrics service
229// into this class.
230class MetricsServiceBase : public HistogramSender {
231 protected:
232  MetricsServiceBase();
233  virtual ~MetricsServiceBase();
234
235  // Check to see if there is a log that needs to be, or is being, transmitted.
236  bool pending_log() const {
237    return pending_log_ || !compressed_log_.empty();
238  }
239
240  // Compress the report log in |input| using bzip2, store the result in
241  // |output|.
242  bool Bzip2Compress(const std::string& input, std::string* output);
243
244  // Discard |pending_log_|, and clear |compressed_log_|. Called after
245  // processing of this log is complete.
246  void DiscardPendingLog();
247
248  // Record complete list of histograms into the current log.
249  // Called when we close a log.
250  void RecordCurrentHistograms();
251
252  // A log that we are currently transmiting, or about to try to transmit.
253  MetricsLogBase* pending_log_;
254
255  // An alternate form of |pending_log_|.  We persistently save this version
256  // into prefs if we can't transmit it.  As a result, sometimes all we have is
257  // the compressed text version.
258  std::string compressed_log_;
259
260  // The log that we are still appending to.
261  MetricsLogBase* current_log_;
262
263 private:
264  // HistogramSender interface (override) methods.
265  virtual void TransmitHistogramDelta(
266      const base::Histogram& histogram,
267      const base::Histogram::SampleSet& snapshot);
268  virtual void InconsistencyDetected(int problem);
269  virtual void UniqueInconsistencyDetected(int problem);
270  virtual void SnapshotProblemResolved(int amount);
271
272  DISALLOW_COPY_AND_ASSIGN(MetricsServiceBase);
273};
274
275#endif  // CHROME_COMMON_METRICS_HELPERS_H_
276