1// Copyright 2018 The Fuchsia 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
5library fuchsia.cobalt;
6
7using fuchsia.mem;
8
9// Cobalt is the Fuchsia service used to log, collect and analyze metrics.
10// The two main pillars of Cobalt are protecting user privacy and providing
11// high-quality, aggregate metrics to serve system and component software
12// developers' needs.
13//
14// This file contains interfaces that allow clients to log Events to
15// Cobalt.
16//
17// To use Cobalt, you must have a Project and one or more Metrics registered
18// with the Cobalt registration system. You must also register one or more
19// Reports in order to see the results of your logging aggregated over
20// all Fuchsia devices. Registration of Projects, Metrics and Reports consists
21// of entries in the YAML files in this repo:
22// https://cobalt-analytics.googlesource.com/config/.
23// In a Garnet checkout that is mapped to //third_party/cobalt_config.
24// Each registered object has an integer ID and those IDs are used as
25// parameters in the methods in this file.
26//
27// While Cobalt's code and registration files are open source, the running
28// system being managed by the Cobalt team is currently intended to be used by
29// software engineers at Google in order to collect metrics in a way that
30// preserves our users' privacy. If you are a Google software engineer
31// please see our internal [user guide](go/fuchsia-cobalt-userguide) or
32// ask for assistance from the Cobalt [team](go/fuchsia-cobalt#comms).
33//
34// Usage: First use LoggerFactory to get a Logger for your
35// project. Then you log Events using the Log*() methods.
36// Events are accumulated by the cobalt FIDL service and periodically
37// Observations, derived from the logged Events, are sent to the Cobalt server,
38// where they are used to generate Reports.
39
40// The maximum size of a single Event is 100 KB.
41const int64 MAX_BYTES_PER_EVENT = 102400;
42
43// Response codes for Logger operations.
44enum Status : int32 {
45    OK = 0;
46
47    // For example the supplied metric id is invalid.
48    INVALID_ARGUMENTS = 1;
49
50    // An attempt was made to log an Event whose seralized size exceeds
51    // MAX_BYTES_PER_EVENT.
52    EVENT_TOO_BIG = 2;
53
54    // Cobalt's local buffer is temporarily full and cannot handle any more
55    // Events at this time. Try again later. This condition should be rare
56    BUFFER_FULL = 3;
57
58    // Catch-all for unexpected errors.
59    INTERNAL_ERROR = -1;
60};
61
62// The release stage of a client's project is used to decide which
63// metrics are allowed to be collected.
64enum ReleaseStage : int32 {
65    // Only metrics targetting GA releases will be sent to the server.
66    GA = 0;
67
68    // Metrics targetting DOGFOOD and GA will be sent to the server.
69    DOGFOOD = 10;
70
71    // Metrics targetting FISHFOOD, DOGFOOD and GA will be sent to the server.
72    FISHFOOD = 20;
73
74    // All metrics will be sent to the server.
75    DEBUG = 99;
76};
77
78// A ProjectProfile is used to describe the client's Cobalt project
79struct ProjectProfile {
80    // The |config| Buffer contains the bytes of a serialized Cobalt config
81    // proto message. This specifies registered Metric and Report definitions
82    // for a single project.
83    fuchsia.mem.Buffer config;
84
85    // The release stage of the client's project.
86    ReleaseStage release_stage;
87};
88
89[Discoverable, Layout = "Simple"]
90// LoggerFactory creates Loggers.
91interface LoggerFactory {
92    // Creates a Logger for the given ProjectProfile.
93    //
94    // |profile| A ProjectProfile describing the Cobalt project that the
95    // returned Logger is for.
96    //
97    // |status| Returns OK on success or INVALID_ARGUMENTS if the project
98    //  profile does not contain a valid Cobalt config with only a single
99    //  project_id.
100    1: CreateLogger(ProjectProfile profile,
101                    request<Logger> logger)
102           -> (Status status);
103
104    // Creates a LoggerSimple for the given ProjectProfile.
105    //
106    // |profile| A ProjectProfile describing the Cobalt project that the
107    // returned Logger is for.
108    //
109    // |status| Returns OK on success or INVALID_ARGUMENTS if the project
110    //  profile does not contain a valid Cobalt config with only a single
111    //  project_id.
112    2: CreateLoggerSimple(ProjectProfile profile,
113                          request<LoggerSimple> logger)
114           -> (Status status);
115};
116
117/////////////////////////////////////////////////////////////////////
118// LoggerBase Interface
119/////////////////////////////////////////////////////////////////////
120
121// LoggerBase and its extensions are used to log Events to the Cobalt system.
122// The Cobalt FIDL service stores the Events locally for some period of time,
123// processes the Events to form Observations, and periodically uploads batches
124// of Observations to the Cobalt server. The Cobalt server processes the
125// Observations and generates Reports. See [TODO(rudominer)] for more
126// description of the Cobalt server and Reports.
127//
128// LoggerBase or one of its extensions is associated with a single Cobalt
129// project.
130//
131// This interface conforms to the Simple layout so that Simple bindings
132// may be generated for it. For the full interfaces, see Logger and LoggerSimple
133// below.
134[Layout = "Simple"]
135interface LoggerBase {
136    // Logs the fact that an event has occurred.
137    //
138    // |metric_id| ID of the metric to use. It must be one of the Metrics
139    // from the ProjectProfile used to obtain this Logger, and it must be of
140    // type EVENT_OCCURRED.
141    //
142    // |event_type_index| The index of the event type that occurred. The indexed
143    //     set of all event types is specified in the metric definition.
144    1: LogEvent(uint32 metric_id, uint32 event_type_index)
145           -> (Status status);
146
147    // Logs that an event has occurred a given number of times.
148    //
149    // |metric_id| ID of the metric to use. It must be one of the Metrics
150    // from the ProjectProfile used to obtain this Logger, and it must be of
151    // type EVENT_COUNT.
152    //
153    // |event_type_index| The index of the event type that occurred. The indexed
154    //     set of all event types is specified in the metric definition.
155    //
156    // |component| Optionally, a component associated with the event may
157    //     also be logged. Any notion of component that makes sense may be
158    //     used or use the empty string if there is no natural notion of
159    //     component.
160    //
161    // |period_duration_micros| Optionally, the period of time over which
162    //     the |count| events occurred may be logged. If this is not
163    //     relevant the value may be set to 0. Otherwise specify the period
164    //     duration as a number of microseconds.
165    //
166    //  |count| The number of times the event occurred. One may choose to
167    //      always set this value to 1 and always set
168    //     |period_duration_micros| to 0 in order to achieve a semantics
169    //     similar to the LogEventOccurred() method, but with a |component|.
170    2: LogEventCount(uint32 metric_id, uint32 event_type_index,
171                     string:64 component, int64 period_duration_micros,
172                     int64 count)
173           -> (Status status);
174
175    // Logs that an event lasted a given amount of time.
176    //
177    // |metric_id| ID of the metric to use. It must be one of the Metrics
178    // from the ProjectProfile used to obtain this Logger, and it must be of
179    // type ELAPSED_TIME.
180    //
181    // |event_type_index| The index of the event type that occurred. The indexed
182    //     set of all event types is specified in the metric definition.
183    //
184    // |component| Optionally, a component associated with the event may
185    //     also be logged. Any notion of component that makes sense may be
186    //     used or use the empty string if there is no natural notion of
187    //     component.
188    //
189    // |elapsed_micros| The elapsed time of the event, specified as a number
190    //     of microseconds.
191    3: LogElapsedTime(uint32 metric_id, uint32 event_type_index,
192                      string:64 component, int64 elapsed_micros)
193           -> (Status status);
194
195    // Logs a measured average frame rate.
196    //
197    // |metric_id| ID of the metric to use. It must be one of the Metrics
198    // from the ProjectProfile used to obtain this Logger, and it must be of
199    // type FRAME_RATE.
200    //
201    // |event_type_index| The index of the event type that associated with the
202    //     frame-rate measurement. The indexed set of all event types is
203    //     specified in the metric definition.
204    //
205    // |component| Optionally, a component associated with the frame-rate
206    //     measurement may also be logged. Any notion of component that makes
207    //     sense may be used or use the empty string if there is no natural
208    //     notion of component.
209    //
210    // |fps| The average-frame rate in frames-per-second.
211    4: LogFrameRate(uint32 metric_id, uint32 event_type_index,
212                    string:64 component, float32 fps)
213           -> (Status status);
214
215    // Logs a measured memory usage.
216    //
217    // |metric_id| ID of the metric to use. It must be one of the Metrics
218    // from the ProjectProfile used to obtain this Logger, and it must be of
219    // type MEMORY_USAGE.
220    //
221    // |event_type_index| The index of the event type associated with the memory
222    //     usage. The indexed set of all event types is specified in the metric
223    //     definition.
224    //
225    // |component| Optionally, a component associated with the memory usage
226    //     may also be logged. Any notion of component that makes sense may be
227    //     used or use the empty string if there is no natural notion of
228    //     component.
229    //
230    // |bytes| The memory used, in bytes.
231    5: LogMemoryUsage(uint32 metric_id, uint32 event_type_index,
232                      string:64 component, int64 bytes)
233           -> (Status status);
234
235    // Logs the fact that a given string was used, in a specific context.
236    // The semantics of the context and the string is specified in the
237    // Metric definition.
238    //
239    //  This method is intended to be used in the following situation:
240    //  * The string s being logged does not contain PII or passwords.
241    //  * The set S of all possible strings that may be logged is large.
242    //    If the set S is small consider using LogEvent() instead.
243    //  * The ultimate data of interest is the statistical distribution of the
244    //    most commonly used strings from S over the population of all Fuchsia
245    //    devices.
246    //
247    // |metric_id| ID of the metric to use. It must be one of the Metrics
248    // from the ProjectProfile used to obtain this Logger, and it must be of
249    // type STRING_USED.
250    //
251    // |s| The string to log. This should be a human-readable string of
252    //      size no more than 256 bytes.
253    6: LogString(uint32 metric_id, string:256 s) -> (Status status);
254
255    // This method is part of Cobalt's helper service for measuring the time
256    // delta between two events that occur in different processes. This starts
257    // the timer. A corresponding invocation of EndTimer() with the same
258    // |timer_id| ends the timer. After both StartTimer() and EnvdTimer() have
259    // been invoked, LogElapsedTime() will be invoked with the difference
260    // between the end timestamp and the start timestamp as the value of
261    // |duration_microseconds|. It is OK if Cobalt receives the EndTimer()
262    // call before the StartTimer() call.
263    //
264    // |metric_id| ID of the metric to use. It must be one of the Metrics
265    // from the ProjectProfile used to obtain this Logger, and it must be of
266    // type ELAPSED_TIME.
267    //
268    // |event_type_index| The index of the event type to associate with the
269    // elapsed time. This is passed to LogElapsedTime()
270    //
271    // |component| Optionally, a component associated with the event may
272    //     also be logged. See the description at LogElapsedTime().
273    //
274    // |timer_id| The ID of the timer being started. This is an arbitrary
275    //     non-empty string provided by the caller and it is the caller's
276    //     responsibility to ensure that Cobalt receives a pair of
277    //     StartTimer(), EndTimer() calls with this id before the timeout
278    //     and without any intervening additional calls to StartTimer()
279    //     or EndTimer() using the same id. Once such a pair is received
280    //     Cobalt will delete the timer with this ID and after that the
281    //     ID may be re-used.
282    //
283    // |timestamp| The timestamp to set as the start of the timer. The units
284    //     must be microseconds. The absolute value does not matter, only the
285    //     difference between the end and start timestamps will be used.
286    //
287    // |timeout_s| The number of seconds Cobalt should wait to receive the
288    //     corresponding EndTimer() call with the same |timer_id|. If
289    //     Cobalt has already received the corresponding EndTimer() call
290    //     before receiving this StartTimer() call then this value is
291    //     ignored as the timeout has already been set by the EndTimer()
292    //     call. If Cobalt does not receive the corresponding EndTimer()
293    //     call before the timeout then the timer will be deleted and
294    //     this invocation of StartTimer() will be forgotten. Must be a
295    //     positive value less than 300.
296    //
297    // |status| Returns OK on success. There are two success cases:
298    //     (i) Cobalt does not currently have any timers with the given
299    //         timer_id. In that case this call creates a new timer with
300    //         the given ID and start timestamp.
301    //     (ii) Cobalt currently has a timer with the given timer_id for
302    //         which it has received exactly one EndTimer() call and no
303    //         StartTimer() calls. In this case Cobalt will delete the
304    //         timer and invoke LogElapsedTime() using the difference
305    //         between the end timestamp and the start timestamp as the
306    //         value of |duration_micors|. It is ok if this value is
307    //         negative.
308    //     Returns INVALID_ARGUMENTS if |timer_id| is empty, the timeout
309    //        is not positive and less than 5 minutes or Cobalt currently
310    //        has a timer with the given timer_ID and it already has a start
311    //        timestamp. In the last case Cobalt will delete the timer with
312    //        the given |timer_id| and this invocation of StartTimer()
313    //        will be forgotten.
314    //     Any error returned by LogElapsedTime() may also be returned by this
315    //     method.
316    7: StartTimer(uint32 metric_id, uint32 event_type_index,
317                  string:64 component, string:64 timer_id,
318                  uint64 timestamp, uint32 timeout_s)
319           -> (Status status);
320
321    // This method is part of Cobalt's helper service for measuring the time
322    // delta between two events that occur in different processes. This ends
323    // the timer. A corresponding invocation of StartTimer() with the same
324    // |timer_id| starts the timer. After both StartTimer() and EnvdTimer() have
325    // been invoked, LogElapsedTime() will be invoked with the difference
326    // between the end timestamp and the start timestamp as the value of
327    // |duration_microseconds|. It is OK if Cobalt receives the EndTimer()
328    // call before the StartTimer() call.
329    //
330    // |timer_id| The ID of the timer being ended. This is an arbitrary
331    //     non-empty string provided by the caller and it is the caller's
332    //     responsibility to ensure that Cobalt receives a pair of
333    //     StartTimer(), EndTimer() calls with this id before the timeout
334    //     and without any intervening additional calls to StartTimer()
335    //     or EndTimer() using the same id. Once such a pair is received
336    //     Cobalt will delete the timer with this ID and after that the
337    //     ID may be re-used.
338    //
339    // |timestamp| The timestamp to set as the end of the timer. The units
340    //     must be microseconds. The absolute value does not matter, only the
341    //     difference between the end and start timestamps will be used.
342    //
343    // |timeout_s| The number of seconds Cobalt should wait to receive the
344    //     corresponding EndTimer() call with the same |timer_id|. If
345    //     Cobalt has already received the corresponding EndTimer() call
346    //     before receiving this StartTimer() call then this value is
347    //     ignored as the timeout has already been set by the EndTimer()
348    //     call. If Cobalt does not receive the corresponding EndTimer()
349    //     call before the timeout then the timer will be deleted and
350    //     this invocation of StartTimer() will be forgotten. Must be a
351    //     positive value less than 300.
352    //
353    // |status| Returns OK on success. There are two success cases:
354    //     (i) Cobalt does not currently have any timers with the given
355    //         timer_id. In that case this call creates a new timer with
356    //         the given ID and end timestamp.
357    //     (ii) Cobalt currently has a timer with the given timer_id for
358    //         which it has received exactly one StartTimer() call and no
359    //         EndTimer() calls. In this case Cobalt will delete the
360    //         timer and invoke LogElapsedTime() using the difference
361    //         between the end timestamp and the start timestamp as the
362    //         value of |duration_micors|. It is ok if this value is
363    //         negative.
364    //     Returns INVALID_ARGUMENTS if |timer_id| is empty, the timeout
365    //        is not positive and less than 5 minutes or Cobalt currently
366    //        has a timer with the given timer_ID and it already has an end
367    //        timestamp. In the last case Cobalt will delete the timer with
368    //        the given |timer_id| and this invocation of EndTimer()
369    //        will be forgotten.
370    //     Any error returned by LogElapsedTime() may also be returned by this
371    //     method.
372    8: EndTimer(string:64 timer_id, uint64 timestamp, uint32 timeout_s)
373           -> (Status status);
374
375    // Method ordinals >= 100 are reserved for sub-interfaces.
376};
377
378/////////////////////////////////////////////////////////////////////
379// Logger Interface
380/////////////////////////////////////////////////////////////////////
381
382// A value for a custom Event. This is used by the method LogCustomEvent().
383struct CustomEventValue {
384    // The name of the Metric dimension this value is for.
385    string dimension_name;
386
387    // The value for that dimension.
388    Value value;
389};
390
391// A value that may be a string, int, double, or index.
392union Value {
393    string string_value;
394    int64 int_value;
395    float64 double_value;
396    uint32 index_value;
397};
398
399// One bucket of histogram. This is used by the method LogIntHistogram().
400struct HistogramBucket {
401    // The index of the bucket. The meaning of the bucket is specified in the
402    // Metric definition.
403    uint32 index;
404
405    // The number of values in that bucket.
406    uint64 count;
407};
408
409// Logger is an extension of the LoggerBase interface that adds some additional
410// methods that do not naturally conform to the Simple layout. We opt for
411// a natural easy-to-understand interface at the cost of not being "Simple".
412// See the interface LoggerSimple below for versions of some of these methods
413// that do conform to the Simple layout.
414interface Logger : LoggerBase {
415    // Logs a histogram over a set of integer buckets. The meaning of the
416    // Metric and the buckets is specified in the Metric definition.
417    //
418    // This method is intended to be used in situations where the client
419    // wishes to aggregate a large number of integer-valued measurements
420    // *in-process*, prior to submitting the data to Cobalt.
421    // One reason a client may wish to do this is that the measurements occur
422    // with very high frequency and it is not practical to make a FIDL call
423    // for each individual measurement.
424    //
425    // |metric_id| ID of the metric to use. It must be one of the Metrics
426    // from the ProjectProfile used to obtain this Logger, and it must be of
427    // type INT_HISTOGRAM.
428    //
429    // |event_type_index| The index of the event type associated with the
430    // integer-valued measurement. The indexed set of all event types is
431    // specified in the metric definition.
432    //
433    // |component| Optionally, a component associated with integer-valued
434    //     measurements may also be logged. Any notion of component that makes
435    //     sense may be used or use the empty string if there is no natural
436    //     notion of component.
437    //
438    // |histogram| The histogram to log. Each HistogramBucket gives the count
439    //     for one bucket of the histogram. The definitions of the buckets is
440    //     given in the Metric definition.
441    100: LogIntHistogram(uint32 metric_id, uint32 event_type_index,
442                         string:64 component, vector<HistogramBucket> histogram)
443             -> (Status status);
444
445    // Logs a custom Event. The semantics of the Metric are specified in the
446    // Metric defintion.
447    //
448    // |metric_id| ID of the metric to use. It must be one of the Metrics
449    // from the ProjectProfile used to obtain this Logger, and it must be of
450    // type CUSTOM.
451    //
452    // |event_values| The values for the custom Event. There is one value for
453    // each dimension of the Metric. The number and types of the values must
454    // be consistent with the dimensions declared in the Metric definition.
455    101: LogCustomEvent(uint32 metric_id,
456                        vector<CustomEventValue> event_values)
457             -> (Status status);
458};
459
460/////////////////////////////////////////////////////////////////////
461// LoggerSimple Interface
462/////////////////////////////////////////////////////////////////////
463
464// LoggerSimple is an extension of the LoggerBase interface that adds some
465// additional methods intended to be used by lower-levels of the Fuchsia system.
466//
467// This interface conforms to the Simple layout so that Simple bindings
468// may be generated for it.
469[Layout = "Simple"]
470interface LoggerSimple : LoggerBase {
471    // Logs a histogram over a set of integer buckets. The meaning of the
472    // Metric and the buckets is specified in the Metric definition.
473    //
474    // See the method LogIntHistogram() in the Logger interface for more
475    // information. This method is similar except that it adheres to the
476    // requirements of Simple layout. Instead of a vector of HistogramBucekts
477    // this version takes two parallel vectors of bucket indices and the
478    // corresponding bucket counts.
479    100: LogIntHistogram(uint32 metric_id, uint32 event_type_index,
480                         string:64 component,
481                         vector<uint32>:100 bucket_indices,
482                         vector<uint64>:100 bucket_counts)
483             -> (Status status);
484};
485
486/////////////////////////////////////////////////////////////////////
487// SystemProfileUpdater Interface
488/////////////////////////////////////////////////////////////////////
489
490// The state of a single experiment on a device or binary.
491struct Experiment {
492    // The id of the experiment as defined by the A/B Experiment framework.
493    uint64 experiment_id;
494    // The id of the experiment arm as definedby the A/B Experiment framework.
495    uint32 arm_id;
496};
497
498[Discoverable]
499// The SystemDataUpdater interface allows callers to update the state of
500// the System Data in Cobalt. This includes the SystemProfile and experiment 
501// state. The changes are global and affect all loggers running on the device.
502interface SystemDataUpdater {
503    // Resets Cobalt's view of the system-wide experiment state and replaces it
504    // with the given values.
505    //
506    // |experiments|  All experiments the device has a notion of and the
507    // arms the device belongs to for each of them. These are the only
508    // experiments the device can collect data for.
509    1: SetExperimentState(vector<Experiment> experiments)
510            -> (Status status);
511};