Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "nsITelemetry.h"
8 : #include "nsIVariant.h"
9 : #include "nsVariant.h"
10 : #include "nsHashKeys.h"
11 : #include "nsBaseHashtable.h"
12 : #include "nsClassHashtable.h"
13 : #include "nsDataHashtable.h"
14 : #include "nsIXPConnect.h"
15 : #include "nsContentUtils.h"
16 : #include "nsThreadUtils.h"
17 : #include "mozilla/StaticMutex.h"
18 : #include "mozilla/Unused.h"
19 :
20 : #include "TelemetryCommon.h"
21 : #include "TelemetryScalar.h"
22 : #include "TelemetryScalarData.h"
23 : #include "ipc/TelemetryComms.h"
24 : #include "ipc/TelemetryIPCAccumulator.h"
25 :
26 : using mozilla::StaticMutex;
27 : using mozilla::StaticMutexAutoLock;
28 : using mozilla::Telemetry::Common::AutoHashtable;
29 : using mozilla::Telemetry::Common::IsExpiredVersion;
30 : using mozilla::Telemetry::Common::CanRecordDataset;
31 : using mozilla::Telemetry::Common::IsInDataset;
32 : using mozilla::Telemetry::Common::LogToBrowserConsole;
33 : using mozilla::Telemetry::Common::GetNameForProcessID;
34 : using mozilla::Telemetry::ScalarActionType;
35 : using mozilla::Telemetry::ScalarID;
36 : using mozilla::Telemetry::ScalarVariant;
37 : using mozilla::Telemetry::ProcessID;
38 :
39 : namespace TelemetryIPCAccumulator = mozilla::TelemetryIPCAccumulator;
40 :
41 : ////////////////////////////////////////////////////////////////////////
42 : ////////////////////////////////////////////////////////////////////////
43 : //
44 : // Naming: there are two kinds of functions in this file:
45 : //
46 : // * Functions named internal_*: these can only be reached via an
47 : // interface function (TelemetryScalar::*). They expect the interface
48 : // function to have acquired |gTelemetryScalarsMutex|, so they do not
49 : // have to be thread-safe.
50 : //
51 : // * Functions named TelemetryScalar::*. This is the external interface.
52 : // Entries and exits to these functions are serialised using
53 : // |gTelemetryScalarsMutex|.
54 : //
55 : // Avoiding races and deadlocks:
56 : //
57 : // All functions in the external interface (TelemetryScalar::*) are
58 : // serialised using the mutex |gTelemetryScalarsMutex|. This means
59 : // that the external interface is thread-safe, and many of the
60 : // internal_* functions can ignore thread safety. But it also brings
61 : // a danger of deadlock if any function in the external interface can
62 : // get back to that interface. That is, we will deadlock on any call
63 : // chain like this
64 : //
65 : // TelemetryScalar::* -> .. any functions .. -> TelemetryScalar::*
66 : //
67 : // To reduce the danger of that happening, observe the following rules:
68 : //
69 : // * No function in TelemetryScalar::* may directly call, nor take the
70 : // address of, any other function in TelemetryScalar::*.
71 : //
72 : // * No internal function internal_* may call, nor take the address
73 : // of, any function in TelemetryScalar::*.
74 :
75 : ////////////////////////////////////////////////////////////////////////
76 : ////////////////////////////////////////////////////////////////////////
77 : //
78 : // PRIVATE TYPES
79 :
80 : namespace {
81 :
82 : const uint32_t kMaximumNumberOfKeys = 100;
83 : const uint32_t kMaximumKeyStringLength = 70;
84 : const uint32_t kMaximumStringValueLength = 50;
85 : const uint32_t kScalarCount =
86 : static_cast<uint32_t>(mozilla::Telemetry::ScalarID::ScalarCount);
87 :
88 : enum class ScalarResult : uint8_t {
89 : // Nothing went wrong.
90 : Ok,
91 : // General Scalar Errors
92 : NotInitialized,
93 : CannotUnpackVariant,
94 : CannotRecordInProcess,
95 : CannotRecordDataset,
96 : KeyedTypeMismatch,
97 : UnknownScalar,
98 : OperationNotSupported,
99 : InvalidType,
100 : InvalidValue,
101 : // Keyed Scalar Errors
102 : KeyIsEmpty,
103 : KeyTooLong,
104 : TooManyKeys,
105 : // String Scalar Errors
106 : StringTooLong,
107 : // Unsigned Scalar Errors
108 : UnsignedNegativeValue,
109 : UnsignedTruncatedValue
110 : };
111 :
112 : typedef nsBaseHashtableET<nsDepCharHashKey, mozilla::Telemetry::ScalarID>
113 : CharPtrEntryType;
114 :
115 : typedef AutoHashtable<CharPtrEntryType> ScalarMapType;
116 :
117 : bool
118 16 : IsValidEnumId(mozilla::Telemetry::ScalarID aID)
119 : {
120 16 : return aID < mozilla::Telemetry::ScalarID::ScalarCount;
121 : }
122 :
123 : /**
124 : * Convert a nsIVariant to a mozilla::Variant, which is used for
125 : * accumulating child process scalars.
126 : */
127 : ScalarResult
128 0 : GetVariantFromIVariant(nsIVariant* aInput, uint32_t aScalarKind,
129 : mozilla::Maybe<ScalarVariant>& aOutput)
130 : {
131 0 : switch (aScalarKind) {
132 : case nsITelemetry::SCALAR_COUNT:
133 : {
134 0 : uint32_t val = 0;
135 0 : nsresult rv = aInput->GetAsUint32(&val);
136 0 : if (NS_FAILED(rv)) {
137 0 : return ScalarResult::CannotUnpackVariant;
138 : }
139 0 : aOutput = mozilla::Some(mozilla::AsVariant(val));
140 0 : break;
141 : }
142 : case nsITelemetry::SCALAR_STRING:
143 : {
144 0 : nsString val;
145 0 : nsresult rv = aInput->GetAsAString(val);
146 0 : if (NS_FAILED(rv)) {
147 0 : return ScalarResult::CannotUnpackVariant;
148 : }
149 0 : aOutput = mozilla::Some(mozilla::AsVariant(val));
150 0 : break;
151 : }
152 : case nsITelemetry::SCALAR_BOOLEAN:
153 : {
154 0 : bool val = false;
155 0 : nsresult rv = aInput->GetAsBool(&val);
156 0 : if (NS_FAILED(rv)) {
157 0 : return ScalarResult::CannotUnpackVariant;
158 : }
159 0 : aOutput = mozilla::Some(mozilla::AsVariant(val));
160 0 : break;
161 : }
162 : default:
163 0 : MOZ_ASSERT(false, "Unknown scalar kind.");
164 : return ScalarResult::UnknownScalar;
165 : }
166 0 : return ScalarResult::Ok;
167 : }
168 :
169 : // Implements the methods for ScalarInfo.
170 : const char *
171 186 : ScalarInfo::name() const
172 : {
173 186 : return &gScalarsStringTable[this->name_offset];
174 : }
175 :
176 : const char *
177 7 : ScalarInfo::expiration() const
178 : {
179 7 : return &gScalarsStringTable[this->expiration_offset];
180 : }
181 :
182 : /**
183 : * The base scalar object, that servers as a common ancestor for storage
184 : * purposes.
185 : */
186 8 : class ScalarBase
187 : {
188 : public:
189 0 : virtual ~ScalarBase() = default;
190 :
191 : // Set, Add and SetMaximum functions as described in the Telemetry IDL.
192 : virtual ScalarResult SetValue(nsIVariant* aValue) = 0;
193 0 : virtual ScalarResult AddValue(nsIVariant* aValue) { return ScalarResult::OperationNotSupported; }
194 0 : virtual ScalarResult SetMaximum(nsIVariant* aValue) { return ScalarResult::OperationNotSupported; }
195 :
196 : // Convenience methods used by the C++ API.
197 0 : virtual void SetValue(uint32_t aValue) { mozilla::Unused << HandleUnsupported(); }
198 0 : virtual ScalarResult SetValue(const nsAString& aValue) { return HandleUnsupported(); }
199 0 : virtual void SetValue(bool aValue) { mozilla::Unused << HandleUnsupported(); }
200 0 : virtual void AddValue(uint32_t aValue) { mozilla::Unused << HandleUnsupported(); }
201 0 : virtual void SetMaximum(uint32_t aValue) { mozilla::Unused << HandleUnsupported(); }
202 :
203 : // GetValue is used to get the value of the scalar when persisting it to JS.
204 : virtual nsresult GetValue(nsCOMPtr<nsIVariant>& aResult) const = 0;
205 :
206 : // To measure the memory stats.
207 : virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const = 0;
208 :
209 : private:
210 : ScalarResult HandleUnsupported() const;
211 : };
212 :
213 : ScalarResult
214 0 : ScalarBase::HandleUnsupported() const
215 : {
216 0 : MOZ_ASSERT(false, "This operation is not support for this scalar type.");
217 : return ScalarResult::OperationNotSupported;
218 : }
219 :
220 : /**
221 : * The implementation for the unsigned int scalar type.
222 : */
223 : class ScalarUnsigned : public ScalarBase
224 : {
225 : public:
226 : using ScalarBase::SetValue;
227 :
228 5 : ScalarUnsigned() : mStorage(0) {};
229 0 : ~ScalarUnsigned() override = default;
230 :
231 : ScalarResult SetValue(nsIVariant* aValue) final;
232 : void SetValue(uint32_t aValue) final;
233 : ScalarResult AddValue(nsIVariant* aValue) final;
234 : void AddValue(uint32_t aValue) final;
235 : ScalarResult SetMaximum(nsIVariant* aValue) final;
236 : void SetMaximum(uint32_t aValue) final;
237 : nsresult GetValue(nsCOMPtr<nsIVariant>& aResult) const final;
238 : size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const final;
239 :
240 : private:
241 : uint32_t mStorage;
242 :
243 : ScalarResult CheckInput(nsIVariant* aValue);
244 :
245 : // Prevent copying.
246 : ScalarUnsigned(const ScalarUnsigned& aOther) = delete;
247 : void operator=(const ScalarUnsigned& aOther) = delete;
248 : };
249 :
250 : ScalarResult
251 0 : ScalarUnsigned::SetValue(nsIVariant* aValue)
252 : {
253 0 : ScalarResult sr = CheckInput(aValue);
254 0 : if (sr == ScalarResult::UnsignedNegativeValue) {
255 0 : return sr;
256 : }
257 :
258 0 : if (NS_FAILED(aValue->GetAsUint32(&mStorage))) {
259 0 : return ScalarResult::InvalidValue;
260 : }
261 0 : return sr;
262 : }
263 :
264 : void
265 0 : ScalarUnsigned::SetValue(uint32_t aValue)
266 : {
267 0 : mStorage = aValue;
268 0 : }
269 :
270 : ScalarResult
271 0 : ScalarUnsigned::AddValue(nsIVariant* aValue)
272 : {
273 0 : ScalarResult sr = CheckInput(aValue);
274 0 : if (sr == ScalarResult::UnsignedNegativeValue) {
275 0 : return sr;
276 : }
277 :
278 0 : uint32_t newAddend = 0;
279 0 : nsresult rv = aValue->GetAsUint32(&newAddend);
280 0 : if (NS_FAILED(rv)) {
281 0 : return ScalarResult::InvalidValue;
282 : }
283 0 : mStorage += newAddend;
284 0 : return sr;
285 : }
286 :
287 : void
288 10 : ScalarUnsigned::AddValue(uint32_t aValue)
289 : {
290 10 : mStorage += aValue;
291 10 : }
292 :
293 : ScalarResult
294 0 : ScalarUnsigned::SetMaximum(nsIVariant* aValue)
295 : {
296 0 : ScalarResult sr = CheckInput(aValue);
297 0 : if (sr == ScalarResult::UnsignedNegativeValue) {
298 0 : return sr;
299 : }
300 :
301 0 : uint32_t newValue = 0;
302 0 : nsresult rv = aValue->GetAsUint32(&newValue);
303 0 : if (NS_FAILED(rv)) {
304 0 : return ScalarResult::InvalidValue;
305 : }
306 0 : if (newValue > mStorage) {
307 0 : mStorage = newValue;
308 : }
309 0 : return sr;
310 : }
311 :
312 : void
313 0 : ScalarUnsigned::SetMaximum(uint32_t aValue)
314 : {
315 0 : if (aValue > mStorage) {
316 0 : mStorage = aValue;
317 : }
318 0 : }
319 :
320 : nsresult
321 0 : ScalarUnsigned::GetValue(nsCOMPtr<nsIVariant>& aResult) const
322 : {
323 0 : nsCOMPtr<nsIWritableVariant> outVar(new nsVariant());
324 0 : nsresult rv = outVar->SetAsUint32(mStorage);
325 0 : if (NS_FAILED(rv)) {
326 0 : return rv;
327 : }
328 0 : aResult = outVar.forget();
329 0 : return NS_OK;
330 : }
331 :
332 : size_t
333 0 : ScalarUnsigned::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
334 : {
335 0 : return aMallocSizeOf(this);
336 : }
337 :
338 : ScalarResult
339 0 : ScalarUnsigned::CheckInput(nsIVariant* aValue)
340 : {
341 : // If this is a floating point value/double, we will probably get truncated.
342 : uint16_t type;
343 0 : aValue->GetDataType(&type);
344 0 : if (type == nsIDataType::VTYPE_FLOAT ||
345 0 : type == nsIDataType::VTYPE_DOUBLE) {
346 0 : return ScalarResult::UnsignedTruncatedValue;
347 : }
348 :
349 : int32_t signedTest;
350 : // If we're able to cast the number to an int, check its sign.
351 : // Warn the user if he's trying to set the unsigned scalar to a negative
352 : // number.
353 0 : if (NS_SUCCEEDED(aValue->GetAsInt32(&signedTest)) &&
354 0 : signedTest < 0) {
355 0 : return ScalarResult::UnsignedNegativeValue;
356 : }
357 0 : return ScalarResult::Ok;
358 : }
359 :
360 : /**
361 : * The implementation for the string scalar type.
362 : */
363 : class ScalarString : public ScalarBase
364 : {
365 : public:
366 : using ScalarBase::SetValue;
367 :
368 0 : ScalarString() : mStorage(EmptyString()) {};
369 0 : ~ScalarString() override = default;
370 :
371 : ScalarResult SetValue(nsIVariant* aValue) final;
372 : ScalarResult SetValue(const nsAString& aValue) final;
373 : nsresult GetValue(nsCOMPtr<nsIVariant>& aResult) const final;
374 : size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const final;
375 :
376 : private:
377 : nsString mStorage;
378 :
379 : // Prevent copying.
380 : ScalarString(const ScalarString& aOther) = delete;
381 : void operator=(const ScalarString& aOther) = delete;
382 : };
383 :
384 : ScalarResult
385 0 : ScalarString::SetValue(nsIVariant* aValue)
386 : {
387 : // Check that we got the correct data type.
388 : uint16_t type;
389 0 : aValue->GetDataType(&type);
390 0 : if (type != nsIDataType::VTYPE_CHAR &&
391 0 : type != nsIDataType::VTYPE_WCHAR &&
392 0 : type != nsIDataType::VTYPE_DOMSTRING &&
393 0 : type != nsIDataType::VTYPE_CHAR_STR &&
394 0 : type != nsIDataType::VTYPE_WCHAR_STR &&
395 0 : type != nsIDataType::VTYPE_STRING_SIZE_IS &&
396 0 : type != nsIDataType::VTYPE_WSTRING_SIZE_IS &&
397 0 : type != nsIDataType::VTYPE_UTF8STRING &&
398 0 : type != nsIDataType::VTYPE_CSTRING &&
399 0 : type != nsIDataType::VTYPE_ASTRING) {
400 0 : return ScalarResult::InvalidType;
401 : }
402 :
403 0 : nsAutoString convertedString;
404 0 : nsresult rv = aValue->GetAsAString(convertedString);
405 0 : if (NS_FAILED(rv)) {
406 0 : return ScalarResult::InvalidValue;
407 : }
408 0 : return SetValue(convertedString);
409 : };
410 :
411 : ScalarResult
412 0 : ScalarString::SetValue(const nsAString& aValue)
413 : {
414 0 : mStorage = Substring(aValue, 0, kMaximumStringValueLength);
415 0 : if (aValue.Length() > kMaximumStringValueLength) {
416 0 : return ScalarResult::StringTooLong;
417 : }
418 0 : return ScalarResult::Ok;
419 : }
420 :
421 : nsresult
422 0 : ScalarString::GetValue(nsCOMPtr<nsIVariant>& aResult) const
423 : {
424 0 : nsCOMPtr<nsIWritableVariant> outVar(new nsVariant());
425 0 : nsresult rv = outVar->SetAsAString(mStorage);
426 0 : if (NS_FAILED(rv)) {
427 0 : return rv;
428 : }
429 0 : aResult = outVar.forget();
430 0 : return NS_OK;
431 : }
432 :
433 : size_t
434 0 : ScalarString::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
435 : {
436 0 : size_t n = aMallocSizeOf(this);
437 0 : n+= mStorage.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
438 0 : return n;
439 : }
440 :
441 : /**
442 : * The implementation for the boolean scalar type.
443 : */
444 : class ScalarBoolean : public ScalarBase
445 : {
446 : public:
447 : using ScalarBase::SetValue;
448 :
449 3 : ScalarBoolean() : mStorage(false) {};
450 0 : ~ScalarBoolean() override = default;
451 :
452 : ScalarResult SetValue(nsIVariant* aValue) final;
453 : void SetValue(bool aValue) final;
454 : nsresult GetValue(nsCOMPtr<nsIVariant>& aResult) const final;
455 : size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const final;
456 :
457 : private:
458 : bool mStorage;
459 :
460 : // Prevent copying.
461 : ScalarBoolean(const ScalarBoolean& aOther) = delete;
462 : void operator=(const ScalarBoolean& aOther) = delete;
463 : };
464 :
465 : ScalarResult
466 0 : ScalarBoolean::SetValue(nsIVariant* aValue)
467 : {
468 : // Check that we got the correct data type.
469 : uint16_t type;
470 0 : aValue->GetDataType(&type);
471 0 : if (type != nsIDataType::VTYPE_BOOL &&
472 0 : type != nsIDataType::VTYPE_INT8 &&
473 0 : type != nsIDataType::VTYPE_INT16 &&
474 0 : type != nsIDataType::VTYPE_INT32 &&
475 0 : type != nsIDataType::VTYPE_INT64 &&
476 0 : type != nsIDataType::VTYPE_UINT8 &&
477 0 : type != nsIDataType::VTYPE_UINT16 &&
478 0 : type != nsIDataType::VTYPE_UINT32 &&
479 0 : type != nsIDataType::VTYPE_UINT64) {
480 0 : return ScalarResult::InvalidType;
481 : }
482 :
483 0 : if (NS_FAILED(aValue->GetAsBool(&mStorage))) {
484 0 : return ScalarResult::InvalidValue;
485 : }
486 0 : return ScalarResult::Ok;
487 : };
488 :
489 : void
490 3 : ScalarBoolean::SetValue(bool aValue)
491 : {
492 3 : mStorage = aValue;
493 3 : }
494 :
495 : nsresult
496 0 : ScalarBoolean::GetValue(nsCOMPtr<nsIVariant>& aResult) const
497 : {
498 0 : nsCOMPtr<nsIWritableVariant> outVar(new nsVariant());
499 0 : nsresult rv = outVar->SetAsBool(mStorage);
500 0 : if (NS_FAILED(rv)) {
501 0 : return rv;
502 : }
503 0 : aResult = outVar.forget();
504 0 : return NS_OK;
505 : }
506 :
507 : size_t
508 0 : ScalarBoolean::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
509 : {
510 0 : return aMallocSizeOf(this);
511 : }
512 :
513 : /**
514 : * Allocate a scalar class given the scalar info.
515 : *
516 : * @param aInfo The informations for the scalar coming from the definition file.
517 : * @return nullptr if the scalar type is unknown, otherwise a valid pointer to the
518 : * scalar type.
519 : */
520 : ScalarBase*
521 8 : internal_ScalarAllocate(uint32_t aScalarKind)
522 : {
523 8 : ScalarBase* scalar = nullptr;
524 8 : switch (aScalarKind) {
525 : case nsITelemetry::SCALAR_COUNT:
526 5 : scalar = new ScalarUnsigned();
527 5 : break;
528 : case nsITelemetry::SCALAR_STRING:
529 0 : scalar = new ScalarString();
530 0 : break;
531 : case nsITelemetry::SCALAR_BOOLEAN:
532 3 : scalar = new ScalarBoolean();
533 3 : break;
534 : default:
535 0 : MOZ_ASSERT(false, "Invalid scalar type");
536 : }
537 8 : return scalar;
538 : }
539 :
540 : /**
541 : * The implementation for the keyed scalar type.
542 : */
543 : class KeyedScalar
544 : {
545 : public:
546 : typedef mozilla::Pair<nsCString, nsCOMPtr<nsIVariant>> KeyValuePair;
547 :
548 1 : explicit KeyedScalar(uint32_t aScalarKind) : mScalarKind(aScalarKind) {};
549 0 : ~KeyedScalar() = default;
550 :
551 : // Set, Add and SetMaximum functions as described in the Telemetry IDL.
552 : // These methods implicitly instantiate a Scalar[*] for each key.
553 : ScalarResult SetValue(const nsAString& aKey, nsIVariant* aValue);
554 : ScalarResult AddValue(const nsAString& aKey, nsIVariant* aValue);
555 : ScalarResult SetMaximum(const nsAString& aKey, nsIVariant* aValue);
556 :
557 : // Convenience methods used by the C++ API.
558 : void SetValue(const nsAString& aKey, uint32_t aValue);
559 : void SetValue(const nsAString& aKey, bool aValue);
560 : void AddValue(const nsAString& aKey, uint32_t aValue);
561 : void SetMaximum(const nsAString& aKey, uint32_t aValue);
562 :
563 : // GetValue is used to get the key-value pairs stored in the keyed scalar
564 : // when persisting it to JS.
565 : nsresult GetValue(nsTArray<KeyValuePair>& aValues) const;
566 :
567 : // To measure the memory stats.
568 : size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
569 :
570 : private:
571 : typedef nsClassHashtable<nsCStringHashKey, ScalarBase> ScalarKeysMapType;
572 :
573 : ScalarKeysMapType mScalarKeys;
574 : const uint32_t mScalarKind;
575 :
576 : ScalarResult GetScalarForKey(const nsAString& aKey, ScalarBase** aRet);
577 : };
578 :
579 : ScalarResult
580 0 : KeyedScalar::SetValue(const nsAString& aKey, nsIVariant* aValue)
581 : {
582 0 : ScalarBase* scalar = nullptr;
583 0 : ScalarResult sr = GetScalarForKey(aKey, &scalar);
584 0 : if (sr != ScalarResult::Ok) {
585 0 : return sr;
586 : }
587 :
588 0 : return scalar->SetValue(aValue);
589 : }
590 :
591 : ScalarResult
592 0 : KeyedScalar::AddValue(const nsAString& aKey, nsIVariant* aValue)
593 : {
594 0 : ScalarBase* scalar = nullptr;
595 0 : ScalarResult sr = GetScalarForKey(aKey, &scalar);
596 0 : if (sr != ScalarResult::Ok) {
597 0 : return sr;
598 : }
599 :
600 0 : return scalar->AddValue(aValue);
601 : }
602 :
603 : ScalarResult
604 0 : KeyedScalar::SetMaximum(const nsAString& aKey, nsIVariant* aValue)
605 : {
606 0 : ScalarBase* scalar = nullptr;
607 0 : ScalarResult sr = GetScalarForKey(aKey, &scalar);
608 0 : if (sr != ScalarResult::Ok) {
609 0 : return sr;
610 : }
611 :
612 0 : return scalar->SetMaximum(aValue);
613 : }
614 :
615 : void
616 0 : KeyedScalar::SetValue(const nsAString& aKey, uint32_t aValue)
617 : {
618 0 : ScalarBase* scalar = nullptr;
619 0 : ScalarResult sr = GetScalarForKey(aKey, &scalar);
620 0 : if (sr != ScalarResult::Ok) {
621 0 : MOZ_ASSERT(false, "Key too long or too many keys are recorded in the scalar.");
622 : return;
623 : }
624 :
625 0 : return scalar->SetValue(aValue);
626 : }
627 :
628 : void
629 2 : KeyedScalar::SetValue(const nsAString& aKey, bool aValue)
630 : {
631 2 : ScalarBase* scalar = nullptr;
632 2 : ScalarResult sr = GetScalarForKey(aKey, &scalar);
633 2 : if (sr != ScalarResult::Ok) {
634 0 : MOZ_ASSERT(false, "Key too long or too many keys are recorded in the scalar.");
635 : return;
636 : }
637 :
638 2 : return scalar->SetValue(aValue);
639 : }
640 :
641 : void
642 0 : KeyedScalar::AddValue(const nsAString& aKey, uint32_t aValue)
643 : {
644 0 : ScalarBase* scalar = nullptr;
645 0 : ScalarResult sr = GetScalarForKey(aKey, &scalar);
646 0 : if (sr != ScalarResult::Ok) {
647 0 : MOZ_ASSERT(false, "Key too long or too many keys are recorded in the scalar.");
648 : return;
649 : }
650 :
651 0 : return scalar->AddValue(aValue);
652 : }
653 :
654 : void
655 0 : KeyedScalar::SetMaximum(const nsAString& aKey, uint32_t aValue)
656 : {
657 0 : ScalarBase* scalar = nullptr;
658 0 : ScalarResult sr = GetScalarForKey(aKey, &scalar);
659 0 : if (sr != ScalarResult::Ok) {
660 0 : MOZ_ASSERT(false, "Key too long or too many keys are recorded in the scalar.");
661 : return;
662 : }
663 :
664 0 : return scalar->SetMaximum(aValue);
665 : }
666 :
667 : /**
668 : * Get a key-value array with the values for the Keyed Scalar.
669 : * @param aValue The array that will hold the key-value pairs.
670 : * @return {nsresult} NS_OK or an error value as reported by the
671 : * the specific scalar objects implementations (e.g.
672 : * ScalarUnsigned).
673 : */
674 : nsresult
675 0 : KeyedScalar::GetValue(nsTArray<KeyValuePair>& aValues) const
676 : {
677 0 : for (auto iter = mScalarKeys.ConstIter(); !iter.Done(); iter.Next()) {
678 0 : ScalarBase* scalar = static_cast<ScalarBase*>(iter.Data());
679 :
680 : // Get the scalar value.
681 0 : nsCOMPtr<nsIVariant> scalarValue;
682 0 : nsresult rv = scalar->GetValue(scalarValue);
683 0 : if (NS_FAILED(rv)) {
684 0 : return rv;
685 : }
686 :
687 : // Append it to value list.
688 0 : aValues.AppendElement(mozilla::MakePair(nsCString(iter.Key()), scalarValue));
689 : }
690 :
691 0 : return NS_OK;
692 : }
693 :
694 : /**
695 : * Get the scalar for the referenced key.
696 : * If there's no such key, instantiate a new Scalar object with the
697 : * same type of the Keyed scalar and create the key.
698 : */
699 : ScalarResult
700 2 : KeyedScalar::GetScalarForKey(const nsAString& aKey, ScalarBase** aRet)
701 : {
702 2 : if (aKey.IsEmpty()) {
703 0 : return ScalarResult::KeyIsEmpty;
704 : }
705 :
706 2 : if (aKey.Length() >= kMaximumKeyStringLength) {
707 0 : return ScalarResult::KeyTooLong;
708 : }
709 :
710 2 : if (mScalarKeys.Count() >= kMaximumNumberOfKeys) {
711 0 : return ScalarResult::TooManyKeys;
712 : }
713 :
714 4 : NS_ConvertUTF16toUTF8 utf8Key(aKey);
715 :
716 2 : ScalarBase* scalar = nullptr;
717 2 : if (mScalarKeys.Get(utf8Key, &scalar)) {
718 0 : *aRet = scalar;
719 0 : return ScalarResult::Ok;
720 : }
721 :
722 2 : scalar = internal_ScalarAllocate(mScalarKind);
723 2 : if (!scalar) {
724 0 : return ScalarResult::InvalidType;
725 : }
726 :
727 2 : mScalarKeys.Put(utf8Key, scalar);
728 :
729 2 : *aRet = scalar;
730 2 : return ScalarResult::Ok;
731 : }
732 :
733 : size_t
734 0 : KeyedScalar::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
735 : {
736 0 : size_t n = aMallocSizeOf(this);
737 0 : for (auto iter = mScalarKeys.Iter(); !iter.Done(); iter.Next()) {
738 0 : ScalarBase* scalar = static_cast<ScalarBase*>(iter.Data());
739 0 : n += scalar->SizeOfIncludingThis(aMallocSizeOf);
740 : }
741 0 : return n;
742 : }
743 :
744 : typedef nsUint32HashKey ScalarIDHashKey;
745 : typedef nsUint32HashKey ProcessIDHashKey;
746 : typedef nsClassHashtable<ScalarIDHashKey, ScalarBase> ScalarStorageMapType;
747 : typedef nsClassHashtable<ScalarIDHashKey, KeyedScalar> KeyedScalarStorageMapType;
748 : typedef nsClassHashtable<ProcessIDHashKey, ScalarStorageMapType> ProcessesScalarsMapType;
749 : typedef nsClassHashtable<ProcessIDHashKey, KeyedScalarStorageMapType> ProcessesKeyedScalarsMapType;
750 :
751 : } // namespace
752 :
753 : ////////////////////////////////////////////////////////////////////////
754 : ////////////////////////////////////////////////////////////////////////
755 : //
756 : // PRIVATE STATE, SHARED BY ALL THREADS
757 :
758 : namespace {
759 :
760 : // Set to true once this global state has been initialized.
761 : bool gInitDone = false;
762 :
763 : bool gCanRecordBase;
764 : bool gCanRecordExtended;
765 :
766 : // The Name -> ID cache map.
767 3 : ScalarMapType gScalarNameIDMap(kScalarCount);
768 :
769 : // The (Process Id -> (Scalar ID -> Scalar Object)) map. This is a nsClassHashtable,
770 : // it owns the scalar instances and takes care of deallocating them when they are
771 : // removed from the map.
772 3 : ProcessesScalarsMapType gScalarStorageMap;
773 : // As above, for the keyed scalars.
774 3 : ProcessesKeyedScalarsMapType gKeyedScalarStorageMap;
775 :
776 : } // namespace
777 :
778 : ////////////////////////////////////////////////////////////////////////
779 : ////////////////////////////////////////////////////////////////////////
780 : //
781 : // PRIVATE: Function that may call JS code.
782 :
783 : // NOTE: the functions in this section all run without protection from
784 : // |gTelemetryScalarsMutex|. If they held the mutex, there would be the
785 : // possibility of deadlock because the JS_ calls that they make may call
786 : // back into the TelemetryScalar interface, hence trying to re-acquire the mutex.
787 : //
788 : // This means that these functions potentially race against threads, but
789 : // that seems preferable to risking deadlock.
790 :
791 : namespace {
792 :
793 : /**
794 : * Converts the error code to a human readable error message and prints it to the
795 : * browser console.
796 : *
797 : * @param aScalarName The name of the scalar that raised the error.
798 : * @param aSr The error code.
799 : */
800 : void
801 0 : internal_LogScalarError(const nsACString& aScalarName, ScalarResult aSr)
802 : {
803 0 : nsAutoString errorMessage;
804 0 : AppendUTF8toUTF16(aScalarName, errorMessage);
805 :
806 0 : switch (aSr) {
807 : case ScalarResult::NotInitialized:
808 0 : errorMessage.Append(NS_LITERAL_STRING(" - Telemetry was not yet initialized."));
809 0 : break;
810 : case ScalarResult::CannotUnpackVariant:
811 0 : errorMessage.Append(NS_LITERAL_STRING(" - Cannot convert the provided JS value to nsIVariant."));
812 0 : break;
813 : case ScalarResult::CannotRecordInProcess:
814 0 : errorMessage.Append(NS_LITERAL_STRING(" - Cannot record the scalar in the current process."));
815 0 : break;
816 : case ScalarResult::KeyedTypeMismatch:
817 0 : errorMessage.Append(NS_LITERAL_STRING(" - Attempting to manage a keyed scalar as a scalar (or vice-versa)."));
818 0 : break;
819 : case ScalarResult::UnknownScalar:
820 0 : errorMessage.Append(NS_LITERAL_STRING(" - Unknown scalar."));
821 0 : break;
822 : case ScalarResult::OperationNotSupported:
823 0 : errorMessage.Append(NS_LITERAL_STRING(" - The requested operation is not supported on this scalar."));
824 0 : break;
825 : case ScalarResult::InvalidType:
826 0 : errorMessage.Append(NS_LITERAL_STRING(" - Attempted to set the scalar to an invalid data type."));
827 0 : break;
828 : case ScalarResult::InvalidValue:
829 0 : errorMessage.Append(NS_LITERAL_STRING(" - Attempted to set the scalar to an incompatible value."));
830 0 : break;
831 : case ScalarResult::StringTooLong:
832 0 : errorMessage.Append(NS_LITERAL_STRING(" - Truncating scalar value to 50 characters."));
833 0 : break;
834 : case ScalarResult::KeyIsEmpty:
835 0 : errorMessage.Append(NS_LITERAL_STRING(" - The key must not be empty."));
836 0 : break;
837 : case ScalarResult::KeyTooLong:
838 0 : errorMessage.Append(NS_LITERAL_STRING(" - The key length must be limited to 70 characters."));
839 0 : break;
840 : case ScalarResult::TooManyKeys:
841 0 : errorMessage.Append(NS_LITERAL_STRING(" - Keyed scalars cannot have more than 100 keys."));
842 0 : break;
843 : case ScalarResult::UnsignedNegativeValue:
844 0 : errorMessage.Append(NS_LITERAL_STRING(" - Trying to set an unsigned scalar to a negative number."));
845 0 : break;
846 : case ScalarResult::UnsignedTruncatedValue:
847 0 : errorMessage.Append(NS_LITERAL_STRING(" - Truncating float/double number."));
848 0 : break;
849 : default:
850 : // Nothing.
851 0 : return;
852 : }
853 :
854 0 : LogToBrowserConsole(nsIScriptError::warningFlag, errorMessage);
855 : }
856 :
857 : } // namespace
858 :
859 : ////////////////////////////////////////////////////////////////////////
860 : ////////////////////////////////////////////////////////////////////////
861 : //
862 : // PRIVATE: thread-unsafe helpers for the external interface
863 :
864 : namespace {
865 :
866 : bool
867 5 : internal_CanRecordBase()
868 : {
869 5 : return gCanRecordBase;
870 : }
871 :
872 : bool
873 3 : internal_CanRecordExtended()
874 : {
875 3 : return gCanRecordExtended;
876 : }
877 :
878 : const ScalarInfo&
879 9 : internal_InfoForScalarID(mozilla::Telemetry::ScalarID aId)
880 : {
881 9 : return gScalars[static_cast<uint32_t>(aId)];
882 : }
883 :
884 : /**
885 : * Check if the given scalar is a keyed scalar.
886 : *
887 : * @param aId The scalar enum.
888 : * @return true if aId refers to a keyed scalar, false otherwise.
889 : */
890 : bool
891 3 : internal_IsKeyedScalar(mozilla::Telemetry::ScalarID aId)
892 : {
893 3 : return internal_InfoForScalarID(aId).keyed;
894 : }
895 :
896 : /**
897 : * Check if we're allowed to record the given scalar in the current
898 : * process.
899 : *
900 : * @param aId The id of the scalar to check.
901 : * @return true if the scalar is allowed to be recorded in the current process, false
902 : * otherwise.
903 : */
904 : bool
905 3 : internal_CanRecordProcess(mozilla::Telemetry::ScalarID aId)
906 : {
907 3 : const ScalarInfo &info = internal_InfoForScalarID(aId);
908 3 : return CanRecordInProcess(info.record_in_processes, XRE_GetProcessType());
909 : }
910 :
911 : bool
912 3 : internal_CanRecordForScalarID(mozilla::Telemetry::ScalarID aId)
913 : {
914 : // Get the scalar info from the id.
915 3 : const ScalarInfo &info = internal_InfoForScalarID(aId);
916 :
917 : // Can we record at all?
918 3 : bool canRecordBase = internal_CanRecordBase();
919 3 : if (!canRecordBase) {
920 0 : return false;
921 : }
922 :
923 3 : bool canRecordDataset = CanRecordDataset(info.dataset,
924 : canRecordBase,
925 6 : internal_CanRecordExtended());
926 3 : if (!canRecordDataset) {
927 0 : return false;
928 : }
929 :
930 3 : return true;
931 : }
932 :
933 : /**
934 : * Check if we are allowed to record the provided scalar.
935 : *
936 : * @param aId The scalar id.
937 : * @param aKeyed Are we attempting to write a keyed scalar?
938 : * @return ScalarResult::Ok if we can record, an error code otherwise.
939 : */
940 : ScalarResult
941 3 : internal_CanRecordScalar(mozilla::Telemetry::ScalarID aId, bool aKeyed)
942 : {
943 : // Make sure that we have a keyed scalar if we are trying to change one.
944 3 : if (internal_IsKeyedScalar(aId) != aKeyed) {
945 0 : return ScalarResult::KeyedTypeMismatch;
946 : }
947 :
948 : // Are we allowed to record this scalar based on the current Telemetry
949 : // settings?
950 3 : if (!internal_CanRecordForScalarID(aId)) {
951 0 : return ScalarResult::CannotRecordDataset;
952 : }
953 :
954 : // Can we record in this process?
955 3 : if (!internal_CanRecordProcess(aId)) {
956 0 : return ScalarResult::CannotRecordInProcess;
957 : }
958 :
959 3 : return ScalarResult::Ok;
960 : }
961 :
962 : /**
963 : * Get the scalar enum id from the scalar name.
964 : *
965 : * @param aName The scalar name.
966 : * @param aId The output variable to contain the enum.
967 : * @return
968 : * NS_ERROR_FAILURE if this was called before init is completed.
969 : * NS_ERROR_INVALID_ARG if the name can't be found in the scalar definitions.
970 : * NS_OK if the scalar was found and aId contains a valid enum id.
971 : */
972 : nsresult
973 0 : internal_GetEnumByScalarName(const nsACString& aName, mozilla::Telemetry::ScalarID* aId)
974 : {
975 0 : if (!gInitDone) {
976 0 : return NS_ERROR_FAILURE;
977 : }
978 :
979 0 : CharPtrEntryType *entry = gScalarNameIDMap.GetEntry(PromiseFlatCString(aName).get());
980 0 : if (!entry) {
981 0 : return NS_ERROR_INVALID_ARG;
982 : }
983 0 : *aId = entry->mData;
984 0 : return NS_OK;
985 : }
986 :
987 : /**
988 : * Get a scalar object by its enum id. This implicitly allocates the scalar
989 : * object in the storage if it wasn't previously allocated.
990 : *
991 : * @param aId The scalar id.
992 : * @param aProcessStorage This drives the selection of the map to use to store
993 : * the scalar data coming from child processes. This is only meaningful when
994 : * this function is called in parent process. If that's the case, if
995 : * this is not |GeckoProcessType_Default|, the process id is used to
996 : * allocate and store the scalars.
997 : * @param aRes The output variable that stores scalar object.
998 : * @return
999 : * NS_ERROR_INVALID_ARG if the scalar id is unknown.
1000 : * NS_ERROR_NOT_AVAILABLE if the scalar is expired.
1001 : * NS_OK if the scalar was found. If that's the case, aResult contains a
1002 : * valid pointer to a scalar type.
1003 : */
1004 : nsresult
1005 11 : internal_GetScalarByEnum(mozilla::Telemetry::ScalarID aId,
1006 : ProcessID aProcessStorage,
1007 : ScalarBase** aRet)
1008 : {
1009 11 : if (!IsValidEnumId(aId)) {
1010 0 : MOZ_ASSERT(false, "Requested a scalar with an invalid id.");
1011 : return NS_ERROR_INVALID_ARG;
1012 : }
1013 :
1014 11 : const uint32_t id = static_cast<uint32_t>(aId);
1015 11 : const ScalarInfo &info = gScalars[id];
1016 :
1017 11 : ScalarBase* scalar = nullptr;
1018 11 : ScalarStorageMapType* scalarStorage = nullptr;
1019 : // Initialize the scalar storage to the parent storage. This will get
1020 : // set to the child storage if needed.
1021 11 : uint32_t storageId = static_cast<uint32_t>(aProcessStorage);
1022 :
1023 : // Get the process-specific storage or create one if it's not
1024 : // available.
1025 11 : if (!gScalarStorageMap.Get(storageId, &scalarStorage)) {
1026 2 : scalarStorage = new ScalarStorageMapType();
1027 2 : gScalarStorageMap.Put(storageId, scalarStorage);
1028 : }
1029 :
1030 : // Check if the scalar is already allocated in the parent or in the child storage.
1031 11 : if (scalarStorage->Get(id, &scalar)) {
1032 5 : *aRet = scalar;
1033 5 : return NS_OK;
1034 : }
1035 :
1036 6 : if (IsExpiredVersion(info.expiration())) {
1037 0 : return NS_ERROR_NOT_AVAILABLE;
1038 : }
1039 :
1040 6 : scalar = internal_ScalarAllocate(info.kind);
1041 6 : if (!scalar) {
1042 0 : return NS_ERROR_INVALID_ARG;
1043 : }
1044 :
1045 6 : scalarStorage->Put(id, scalar);
1046 6 : *aRet = scalar;
1047 6 : return NS_OK;
1048 : }
1049 :
1050 : /**
1051 : * Update the scalar with the provided value. This is used by the JS API.
1052 : *
1053 : * @param aName The scalar name.
1054 : * @param aType The action type for updating the scalar.
1055 : * @param aValue The value to use for updating the scalar.
1056 : * @return a ScalarResult error value.
1057 : */
1058 : ScalarResult
1059 0 : internal_UpdateScalar(const nsACString& aName, ScalarActionType aType,
1060 : nsIVariant* aValue)
1061 : {
1062 : mozilla::Telemetry::ScalarID id;
1063 0 : nsresult rv = internal_GetEnumByScalarName(aName, &id);
1064 0 : if (NS_FAILED(rv)) {
1065 0 : return (rv == NS_ERROR_FAILURE) ?
1066 0 : ScalarResult::NotInitialized : ScalarResult::UnknownScalar;
1067 : }
1068 :
1069 0 : ScalarResult sr = internal_CanRecordScalar(id, false);
1070 0 : if (sr != ScalarResult::Ok) {
1071 0 : if (sr == ScalarResult::CannotRecordDataset) {
1072 0 : return ScalarResult::Ok;
1073 : }
1074 0 : return sr;
1075 : }
1076 :
1077 : // Accumulate in the child process if needed.
1078 0 : if (!XRE_IsParentProcess()) {
1079 0 : const ScalarInfo &info = gScalars[static_cast<uint32_t>(id)];
1080 : // Convert the nsIVariant to a Variant.
1081 0 : mozilla::Maybe<ScalarVariant> variantValue;
1082 0 : sr = GetVariantFromIVariant(aValue, info.kind, variantValue);
1083 0 : if (sr != ScalarResult::Ok) {
1084 0 : MOZ_ASSERT(false, "Unable to convert nsIVariant to mozilla::Variant.");
1085 : return sr;
1086 : }
1087 0 : TelemetryIPCAccumulator::RecordChildScalarAction(id, aType, variantValue.ref());
1088 0 : return ScalarResult::Ok;
1089 : }
1090 :
1091 : // Finally get the scalar.
1092 0 : ScalarBase* scalar = nullptr;
1093 0 : rv = internal_GetScalarByEnum(id, ProcessID::Parent, &scalar);
1094 0 : if (NS_FAILED(rv)) {
1095 : // Don't throw on expired scalars.
1096 0 : if (rv == NS_ERROR_NOT_AVAILABLE) {
1097 0 : return ScalarResult::Ok;
1098 : }
1099 0 : return ScalarResult::UnknownScalar;
1100 : }
1101 :
1102 0 : if (aType == ScalarActionType::eAdd) {
1103 0 : return scalar->AddValue(aValue);
1104 : }
1105 0 : if (aType == ScalarActionType::eSet) {
1106 0 : return scalar->SetValue(aValue);
1107 : }
1108 :
1109 0 : return scalar->SetMaximum(aValue);
1110 : }
1111 :
1112 : } // namespace
1113 :
1114 :
1115 :
1116 : ////////////////////////////////////////////////////////////////////////
1117 : ////////////////////////////////////////////////////////////////////////
1118 : //
1119 : // PRIVATE: thread-unsafe helpers for the keyed scalars
1120 :
1121 : namespace {
1122 :
1123 : /**
1124 : * Get a keyed scalar object by its enum id. This implicitly allocates the keyed
1125 : * scalar object in the storage if it wasn't previously allocated.
1126 : *
1127 : * @param aId The scalar id.
1128 : * @param aProcessStorage This drives the selection of the map to use to store
1129 : * the scalar data coming from child processes. This is only meaningful when
1130 : * this function is called in parent process. If that's the case, if
1131 : * this is not |GeckoProcessType_Default|, the process id is used to
1132 : * allocate and store the scalars.
1133 : * @param aRet The output variable that stores scalar object.
1134 : * @return
1135 : * NS_ERROR_INVALID_ARG if the scalar id is unknown or a this is a keyed string
1136 : * scalar.
1137 : * NS_ERROR_NOT_AVAILABLE if the scalar is expired.
1138 : * NS_OK if the scalar was found. If that's the case, aResult contains a
1139 : * valid pointer to a scalar type.
1140 : */
1141 : nsresult
1142 2 : internal_GetKeyedScalarByEnum(mozilla::Telemetry::ScalarID aId,
1143 : ProcessID aProcessStorage,
1144 : KeyedScalar** aRet)
1145 : {
1146 2 : if (!IsValidEnumId(aId)) {
1147 0 : MOZ_ASSERT(false, "Requested a keyed scalar with an invalid id.");
1148 : return NS_ERROR_INVALID_ARG;
1149 : }
1150 :
1151 2 : const uint32_t id = static_cast<uint32_t>(aId);
1152 2 : const ScalarInfo &info = gScalars[id];
1153 :
1154 2 : KeyedScalar* scalar = nullptr;
1155 2 : KeyedScalarStorageMapType* scalarStorage = nullptr;
1156 : // Initialize the scalar storage to the parent storage. This will get
1157 : // set to the child storage if needed.
1158 2 : uint32_t storageId = static_cast<uint32_t>(aProcessStorage);
1159 :
1160 : // Get the process-specific storage or create one if it's not
1161 : // available.
1162 2 : if (!gKeyedScalarStorageMap.Get(storageId, &scalarStorage)) {
1163 1 : scalarStorage = new KeyedScalarStorageMapType();
1164 1 : gKeyedScalarStorageMap.Put(storageId, scalarStorage);
1165 : }
1166 :
1167 2 : if (scalarStorage->Get(id, &scalar)) {
1168 1 : *aRet = scalar;
1169 1 : return NS_OK;
1170 : }
1171 :
1172 1 : if (IsExpiredVersion(info.expiration())) {
1173 0 : return NS_ERROR_NOT_AVAILABLE;
1174 : }
1175 :
1176 : // We don't currently support keyed string scalars. Disable them.
1177 1 : if (info.kind == nsITelemetry::SCALAR_STRING) {
1178 0 : MOZ_ASSERT(false, "Keyed string scalars are not currently supported.");
1179 : return NS_ERROR_INVALID_ARG;
1180 : }
1181 :
1182 1 : scalar = new KeyedScalar(info.kind);
1183 1 : if (!scalar) {
1184 0 : return NS_ERROR_INVALID_ARG;
1185 : }
1186 :
1187 1 : scalarStorage->Put(id, scalar);
1188 1 : *aRet = scalar;
1189 1 : return NS_OK;
1190 : }
1191 :
1192 : /**
1193 : * Update the keyed scalar with the provided value. This is used by the JS API.
1194 : *
1195 : * @param aName The scalar name.
1196 : * @param aKey The key name.
1197 : * @param aType The action type for updating the scalar.
1198 : * @param aValue The value to use for updating the scalar.
1199 : * @return a ScalarResult error value.
1200 : */
1201 : ScalarResult
1202 0 : internal_UpdateKeyedScalar(const nsACString& aName, const nsAString& aKey,
1203 : ScalarActionType aType, nsIVariant* aValue)
1204 : {
1205 : mozilla::Telemetry::ScalarID id;
1206 0 : nsresult rv = internal_GetEnumByScalarName(aName, &id);
1207 0 : if (NS_FAILED(rv)) {
1208 0 : return (rv == NS_ERROR_FAILURE) ?
1209 0 : ScalarResult::NotInitialized : ScalarResult::UnknownScalar;
1210 : }
1211 :
1212 0 : ScalarResult sr = internal_CanRecordScalar(id, true);
1213 0 : if (sr != ScalarResult::Ok) {
1214 0 : if (sr == ScalarResult::CannotRecordDataset) {
1215 0 : return ScalarResult::Ok;
1216 : }
1217 0 : return sr;
1218 : }
1219 :
1220 : // Accumulate in the child process if needed.
1221 0 : if (!XRE_IsParentProcess()) {
1222 0 : const ScalarInfo &info = gScalars[static_cast<uint32_t>(id)];
1223 : // Convert the nsIVariant to a Variant.
1224 0 : mozilla::Maybe<ScalarVariant> variantValue;
1225 0 : sr = GetVariantFromIVariant(aValue, info.kind, variantValue);
1226 0 : if (sr != ScalarResult::Ok) {
1227 0 : MOZ_ASSERT(false, "Unable to convert nsIVariant to mozilla::Variant.");
1228 : return sr;
1229 : }
1230 0 : TelemetryIPCAccumulator::RecordChildKeyedScalarAction(id, aKey, aType, variantValue.ref());
1231 0 : return ScalarResult::Ok;
1232 : }
1233 :
1234 : // Finally get the scalar.
1235 0 : KeyedScalar* scalar = nullptr;
1236 0 : rv = internal_GetKeyedScalarByEnum(id, ProcessID::Parent, &scalar);
1237 0 : if (NS_FAILED(rv)) {
1238 : // Don't throw on expired scalars.
1239 0 : if (rv == NS_ERROR_NOT_AVAILABLE) {
1240 0 : return ScalarResult::Ok;
1241 : }
1242 0 : return ScalarResult::UnknownScalar;
1243 : }
1244 :
1245 0 : if (aType == ScalarActionType::eAdd) {
1246 0 : return scalar->AddValue(aKey, aValue);
1247 : }
1248 0 : if (aType == ScalarActionType::eSet) {
1249 0 : return scalar->SetValue(aKey, aValue);
1250 : }
1251 :
1252 0 : return scalar->SetMaximum(aKey, aValue);
1253 : }
1254 :
1255 : } // namespace
1256 :
1257 : ////////////////////////////////////////////////////////////////////////
1258 : ////////////////////////////////////////////////////////////////////////
1259 : //
1260 : // EXTERNALLY VISIBLE FUNCTIONS in namespace TelemetryScalars::
1261 :
1262 : // This is a StaticMutex rather than a plain Mutex (1) so that
1263 : // it gets initialised in a thread-safe manner the first time
1264 : // it is used, and (2) because it is never de-initialised, and
1265 : // a normal Mutex would show up as a leak in BloatView. StaticMutex
1266 : // also has the "OffTheBooks" property, so it won't show as a leak
1267 : // in BloatView.
1268 : // Another reason to use a StaticMutex instead of a plain Mutex is
1269 : // that, due to the nature of Telemetry, we cannot rely on having a
1270 : // mutex initialized in InitializeGlobalState. Unfortunately, we
1271 : // cannot make sure that no other function is called before this point.
1272 3 : static StaticMutex gTelemetryScalarsMutex;
1273 :
1274 : void
1275 3 : TelemetryScalar::InitializeGlobalState(bool aCanRecordBase, bool aCanRecordExtended)
1276 : {
1277 6 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
1278 3 : MOZ_ASSERT(!gInitDone, "TelemetryScalar::InitializeGlobalState "
1279 : "may only be called once");
1280 :
1281 3 : gCanRecordBase = aCanRecordBase;
1282 3 : gCanRecordExtended = aCanRecordExtended;
1283 :
1284 : // Populate the static scalar name->id cache. Note that the scalar names are
1285 : // statically allocated and come from the automatically generated TelemetryScalarData.h.
1286 3 : uint32_t scalarCount = static_cast<uint32_t>(mozilla::Telemetry::ScalarID::ScalarCount);
1287 189 : for (uint32_t i = 0; i < scalarCount; i++) {
1288 186 : CharPtrEntryType *entry = gScalarNameIDMap.PutEntry(gScalars[i].name());
1289 186 : entry->mData = static_cast<mozilla::Telemetry::ScalarID>(i);
1290 : }
1291 :
1292 : #ifdef DEBUG
1293 3 : gScalarNameIDMap.MarkImmutable();
1294 : #endif
1295 3 : gInitDone = true;
1296 3 : }
1297 :
1298 : void
1299 0 : TelemetryScalar::DeInitializeGlobalState()
1300 : {
1301 0 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
1302 0 : gCanRecordBase = false;
1303 0 : gCanRecordExtended = false;
1304 0 : gScalarNameIDMap.Clear();
1305 0 : gScalarStorageMap.Clear();
1306 0 : gKeyedScalarStorageMap.Clear();
1307 0 : gInitDone = false;
1308 0 : }
1309 :
1310 : void
1311 0 : TelemetryScalar::SetCanRecordBase(bool b)
1312 : {
1313 0 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
1314 0 : gCanRecordBase = b;
1315 0 : }
1316 :
1317 : void
1318 3 : TelemetryScalar::SetCanRecordExtended(bool b) {
1319 6 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
1320 3 : gCanRecordExtended = b;
1321 3 : }
1322 :
1323 : /**
1324 : * Adds the value to the given scalar.
1325 : *
1326 : * @param aName The scalar name.
1327 : * @param aVal The numeric value to add to the scalar.
1328 : * @param aCx The JS context.
1329 : * @return NS_OK (always) so that the JS API call doesn't throw. In case of errors,
1330 : * a warning level message is printed in the browser console.
1331 : */
1332 : nsresult
1333 0 : TelemetryScalar::Add(const nsACString& aName, JS::HandleValue aVal, JSContext* aCx)
1334 : {
1335 : // Unpack the aVal to nsIVariant. This uses the JS context.
1336 0 : nsCOMPtr<nsIVariant> unpackedVal;
1337 : nsresult rv =
1338 0 : nsContentUtils::XPConnect()->JSToVariant(aCx, aVal, getter_AddRefs(unpackedVal));
1339 0 : if (NS_FAILED(rv)) {
1340 0 : internal_LogScalarError(aName, ScalarResult::CannotUnpackVariant);
1341 0 : return NS_OK;
1342 : }
1343 :
1344 : ScalarResult sr;
1345 : {
1346 0 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
1347 0 : sr = internal_UpdateScalar(aName, ScalarActionType::eAdd, unpackedVal);
1348 : }
1349 :
1350 : // Warn the user about the error if we need to.
1351 0 : if (sr != ScalarResult::Ok) {
1352 0 : internal_LogScalarError(aName, sr);
1353 : }
1354 :
1355 0 : return NS_OK;
1356 : }
1357 :
1358 : /**
1359 : * Adds the value to the given scalar.
1360 : *
1361 : * @param aName The scalar name.
1362 : * @param aKey The key name.
1363 : * @param aVal The numeric value to add to the scalar.
1364 : * @param aCx The JS context.
1365 : * @return NS_OK (always) so that the JS API call doesn't throw. In case of errors,
1366 : * a warning level message is printed in the browser console.
1367 : */
1368 : nsresult
1369 0 : TelemetryScalar::Add(const nsACString& aName, const nsAString& aKey, JS::HandleValue aVal,
1370 : JSContext* aCx)
1371 : {
1372 : // Unpack the aVal to nsIVariant. This uses the JS context.
1373 0 : nsCOMPtr<nsIVariant> unpackedVal;
1374 : nsresult rv =
1375 0 : nsContentUtils::XPConnect()->JSToVariant(aCx, aVal, getter_AddRefs(unpackedVal));
1376 0 : if (NS_FAILED(rv)) {
1377 0 : internal_LogScalarError(aName, ScalarResult::CannotUnpackVariant);
1378 0 : return NS_OK;
1379 : }
1380 :
1381 : ScalarResult sr;
1382 : {
1383 0 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
1384 0 : sr = internal_UpdateKeyedScalar(aName, aKey, ScalarActionType::eAdd, unpackedVal);
1385 : }
1386 :
1387 : // Warn the user about the error if we need to.
1388 0 : if (sr != ScalarResult::Ok) {
1389 0 : internal_LogScalarError(aName, sr);
1390 : }
1391 :
1392 0 : return NS_OK;
1393 : }
1394 :
1395 : /**
1396 : * Adds the value to the given scalar.
1397 : *
1398 : * @param aId The scalar enum id.
1399 : * @param aVal The numeric value to add to the scalar.
1400 : */
1401 : void
1402 0 : TelemetryScalar::Add(mozilla::Telemetry::ScalarID aId, uint32_t aValue)
1403 : {
1404 0 : if (NS_WARN_IF(!IsValidEnumId(aId))) {
1405 0 : MOZ_ASSERT_UNREACHABLE("Scalar usage requires valid ids.");
1406 : return;
1407 : }
1408 :
1409 0 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
1410 :
1411 0 : if (internal_CanRecordScalar(aId, false) != ScalarResult::Ok) {
1412 : // We can't record this scalar. Bail out.
1413 0 : return;
1414 : }
1415 :
1416 : // Accumulate in the child process if needed.
1417 0 : if (!XRE_IsParentProcess()) {
1418 : TelemetryIPCAccumulator::RecordChildScalarAction(aId, ScalarActionType::eAdd,
1419 0 : ScalarVariant(aValue));
1420 0 : return;
1421 : }
1422 :
1423 0 : ScalarBase* scalar = nullptr;
1424 0 : nsresult rv = internal_GetScalarByEnum(aId, ProcessID::Parent, &scalar);
1425 0 : if (NS_FAILED(rv)) {
1426 0 : return;
1427 : }
1428 :
1429 0 : scalar->AddValue(aValue);
1430 : }
1431 :
1432 : /**
1433 : * Adds the value to the given keyed scalar.
1434 : *
1435 : * @param aId The scalar enum id.
1436 : * @param aKey The key name.
1437 : * @param aVal The numeric value to add to the scalar.
1438 : */
1439 : void
1440 0 : TelemetryScalar::Add(mozilla::Telemetry::ScalarID aId, const nsAString& aKey,
1441 : uint32_t aValue)
1442 : {
1443 0 : if (NS_WARN_IF(!IsValidEnumId(aId))) {
1444 0 : MOZ_ASSERT_UNREACHABLE("Scalar usage requires valid ids.");
1445 : return;
1446 : }
1447 :
1448 0 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
1449 :
1450 0 : if (internal_CanRecordScalar(aId, true) != ScalarResult::Ok) {
1451 : // We can't record this scalar. Bail out.
1452 0 : return;
1453 : }
1454 :
1455 : // Accumulate in the child process if needed.
1456 0 : if (!XRE_IsParentProcess()) {
1457 : TelemetryIPCAccumulator::RecordChildKeyedScalarAction(
1458 0 : aId, aKey, ScalarActionType::eAdd, ScalarVariant(aValue));
1459 0 : return;
1460 : }
1461 :
1462 0 : KeyedScalar* scalar = nullptr;
1463 0 : nsresult rv = internal_GetKeyedScalarByEnum(aId, ProcessID::Parent, &scalar);
1464 0 : if (NS_FAILED(rv)) {
1465 0 : return;
1466 : }
1467 :
1468 0 : scalar->AddValue(aKey, aValue);
1469 : }
1470 :
1471 : /**
1472 : * Sets the scalar to the given value.
1473 : *
1474 : * @param aName The scalar name.
1475 : * @param aVal The value to set the scalar to.
1476 : * @param aCx The JS context.
1477 : * @return NS_OK (always) so that the JS API call doesn't throw. In case of errors,
1478 : * a warning level message is printed in the browser console.
1479 : */
1480 : nsresult
1481 0 : TelemetryScalar::Set(const nsACString& aName, JS::HandleValue aVal, JSContext* aCx)
1482 : {
1483 : // Unpack the aVal to nsIVariant. This uses the JS context.
1484 0 : nsCOMPtr<nsIVariant> unpackedVal;
1485 : nsresult rv =
1486 0 : nsContentUtils::XPConnect()->JSToVariant(aCx, aVal, getter_AddRefs(unpackedVal));
1487 0 : if (NS_FAILED(rv)) {
1488 0 : internal_LogScalarError(aName, ScalarResult::CannotUnpackVariant);
1489 0 : return NS_OK;
1490 : }
1491 :
1492 : ScalarResult sr;
1493 : {
1494 0 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
1495 0 : sr = internal_UpdateScalar(aName, ScalarActionType::eSet, unpackedVal);
1496 : }
1497 :
1498 : // Warn the user about the error if we need to.
1499 0 : if (sr != ScalarResult::Ok) {
1500 0 : internal_LogScalarError(aName, sr);
1501 : }
1502 :
1503 0 : return NS_OK;
1504 : }
1505 :
1506 : /**
1507 : * Sets the keyed scalar to the given value.
1508 : *
1509 : * @param aName The scalar name.
1510 : * @param aKey The key name.
1511 : * @param aVal The value to set the scalar to.
1512 : * @param aCx The JS context.
1513 : * @return NS_OK (always) so that the JS API call doesn't throw. In case of errors,
1514 : * a warning level message is printed in the browser console.
1515 : */
1516 : nsresult
1517 0 : TelemetryScalar::Set(const nsACString& aName, const nsAString& aKey, JS::HandleValue aVal,
1518 : JSContext* aCx)
1519 : {
1520 : // Unpack the aVal to nsIVariant. This uses the JS context.
1521 0 : nsCOMPtr<nsIVariant> unpackedVal;
1522 : nsresult rv =
1523 0 : nsContentUtils::XPConnect()->JSToVariant(aCx, aVal, getter_AddRefs(unpackedVal));
1524 0 : if (NS_FAILED(rv)) {
1525 0 : internal_LogScalarError(aName, ScalarResult::CannotUnpackVariant);
1526 0 : return NS_OK;
1527 : }
1528 :
1529 : ScalarResult sr;
1530 : {
1531 0 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
1532 0 : sr = internal_UpdateKeyedScalar(aName, aKey, ScalarActionType::eSet, unpackedVal);
1533 : }
1534 :
1535 : // Warn the user about the error if we need to.
1536 0 : if (sr != ScalarResult::Ok) {
1537 0 : internal_LogScalarError(aName, sr);
1538 : }
1539 :
1540 0 : return NS_OK;
1541 : }
1542 :
1543 : /**
1544 : * Sets the scalar to the given numeric value.
1545 : *
1546 : * @param aId The scalar enum id.
1547 : * @param aValue The numeric, unsigned value to set the scalar to.
1548 : */
1549 : void
1550 0 : TelemetryScalar::Set(mozilla::Telemetry::ScalarID aId, uint32_t aValue)
1551 : {
1552 0 : if (NS_WARN_IF(!IsValidEnumId(aId))) {
1553 0 : MOZ_ASSERT_UNREACHABLE("Scalar usage requires valid ids.");
1554 : return;
1555 : }
1556 :
1557 0 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
1558 :
1559 0 : if (internal_CanRecordScalar(aId, false) != ScalarResult::Ok) {
1560 : // We can't record this scalar. Bail out.
1561 0 : return;
1562 : }
1563 :
1564 : // Accumulate in the child process if needed.
1565 0 : if (!XRE_IsParentProcess()) {
1566 : TelemetryIPCAccumulator::RecordChildScalarAction(aId, ScalarActionType::eSet,
1567 0 : ScalarVariant(aValue));
1568 0 : return;
1569 : }
1570 :
1571 0 : ScalarBase* scalar = nullptr;
1572 0 : nsresult rv = internal_GetScalarByEnum(aId, ProcessID::Parent, &scalar);
1573 0 : if (NS_FAILED(rv)) {
1574 0 : return;
1575 : }
1576 :
1577 0 : scalar->SetValue(aValue);
1578 : }
1579 :
1580 : /**
1581 : * Sets the scalar to the given string value.
1582 : *
1583 : * @param aId The scalar enum id.
1584 : * @param aValue The string value to set the scalar to.
1585 : */
1586 : void
1587 0 : TelemetryScalar::Set(mozilla::Telemetry::ScalarID aId, const nsAString& aValue)
1588 : {
1589 0 : if (NS_WARN_IF(!IsValidEnumId(aId))) {
1590 0 : MOZ_ASSERT_UNREACHABLE("Scalar usage requires valid ids.");
1591 : return;
1592 : }
1593 :
1594 0 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
1595 :
1596 0 : if (internal_CanRecordScalar(aId, false) != ScalarResult::Ok) {
1597 : // We can't record this scalar. Bail out.
1598 0 : return;
1599 : }
1600 :
1601 : // Accumulate in the child process if needed.
1602 0 : if (!XRE_IsParentProcess()) {
1603 : TelemetryIPCAccumulator::RecordChildScalarAction(aId, ScalarActionType::eSet,
1604 0 : ScalarVariant(nsString(aValue)));
1605 0 : return;
1606 : }
1607 :
1608 0 : ScalarBase* scalar = nullptr;
1609 0 : nsresult rv = internal_GetScalarByEnum(aId, ProcessID::Parent, &scalar);
1610 0 : if (NS_FAILED(rv)) {
1611 0 : return;
1612 : }
1613 :
1614 0 : scalar->SetValue(aValue);
1615 : }
1616 :
1617 : /**
1618 : * Sets the scalar to the given boolean value.
1619 : *
1620 : * @param aId The scalar enum id.
1621 : * @param aValue The boolean value to set the scalar to.
1622 : */
1623 : void
1624 1 : TelemetryScalar::Set(mozilla::Telemetry::ScalarID aId, bool aValue)
1625 : {
1626 1 : if (NS_WARN_IF(!IsValidEnumId(aId))) {
1627 0 : MOZ_ASSERT_UNREACHABLE("Scalar usage requires valid ids.");
1628 : return;
1629 : }
1630 :
1631 2 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
1632 :
1633 1 : if (internal_CanRecordScalar(aId, false) != ScalarResult::Ok) {
1634 : // We can't record this scalar. Bail out.
1635 0 : return;
1636 : }
1637 :
1638 : // Accumulate in the child process if needed.
1639 1 : if (!XRE_IsParentProcess()) {
1640 : TelemetryIPCAccumulator::RecordChildScalarAction(aId, ScalarActionType::eSet,
1641 0 : ScalarVariant(aValue));
1642 0 : return;
1643 : }
1644 :
1645 1 : ScalarBase* scalar = nullptr;
1646 1 : nsresult rv = internal_GetScalarByEnum(aId, ProcessID::Parent, &scalar);
1647 1 : if (NS_FAILED(rv)) {
1648 0 : return;
1649 : }
1650 :
1651 1 : scalar->SetValue(aValue);
1652 : }
1653 :
1654 : /**
1655 : * Sets the keyed scalar to the given numeric value.
1656 : *
1657 : * @param aId The scalar enum id.
1658 : * @param aKey The scalar key.
1659 : * @param aValue The numeric, unsigned value to set the scalar to.
1660 : */
1661 : void
1662 0 : TelemetryScalar::Set(mozilla::Telemetry::ScalarID aId, const nsAString& aKey,
1663 : uint32_t aValue)
1664 : {
1665 0 : if (NS_WARN_IF(!IsValidEnumId(aId))) {
1666 0 : MOZ_ASSERT_UNREACHABLE("Scalar usage requires valid ids.");
1667 : return;
1668 : }
1669 :
1670 0 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
1671 :
1672 0 : if (internal_CanRecordScalar(aId, true) != ScalarResult::Ok) {
1673 : // We can't record this scalar. Bail out.
1674 0 : return;
1675 : }
1676 :
1677 : // Accumulate in the child process if needed.
1678 0 : if (!XRE_IsParentProcess()) {
1679 : TelemetryIPCAccumulator::RecordChildKeyedScalarAction(
1680 0 : aId, aKey, ScalarActionType::eSet, ScalarVariant(aValue));
1681 0 : return;
1682 : }
1683 :
1684 0 : KeyedScalar* scalar = nullptr;
1685 0 : nsresult rv = internal_GetKeyedScalarByEnum(aId, ProcessID::Parent, &scalar);
1686 0 : if (NS_FAILED(rv)) {
1687 0 : return;
1688 : }
1689 :
1690 0 : scalar->SetValue(aKey, aValue);
1691 : }
1692 :
1693 : /**
1694 : * Sets the scalar to the given boolean value.
1695 : *
1696 : * @param aId The scalar enum id.
1697 : * @param aKey The scalar key.
1698 : * @param aValue The boolean value to set the scalar to.
1699 : */
1700 : void
1701 2 : TelemetryScalar::Set(mozilla::Telemetry::ScalarID aId, const nsAString& aKey,
1702 : bool aValue)
1703 : {
1704 2 : if (NS_WARN_IF(!IsValidEnumId(aId))) {
1705 0 : MOZ_ASSERT_UNREACHABLE("Scalar usage requires valid ids.");
1706 : return;
1707 : }
1708 :
1709 4 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
1710 :
1711 2 : if (internal_CanRecordScalar(aId, true) != ScalarResult::Ok) {
1712 : // We can't record this scalar. Bail out.
1713 0 : return;
1714 : }
1715 :
1716 : // Accumulate in the child process if needed.
1717 2 : if (!XRE_IsParentProcess()) {
1718 : TelemetryIPCAccumulator::RecordChildKeyedScalarAction(
1719 0 : aId, aKey, ScalarActionType::eSet, ScalarVariant(aValue));
1720 0 : return;
1721 : }
1722 :
1723 2 : KeyedScalar* scalar = nullptr;
1724 2 : nsresult rv = internal_GetKeyedScalarByEnum(aId, ProcessID::Parent, &scalar);
1725 2 : if (NS_FAILED(rv)) {
1726 0 : return;
1727 : }
1728 :
1729 2 : scalar->SetValue(aKey, aValue);
1730 : }
1731 :
1732 : /**
1733 : * Sets the scalar to the maximum of the current and the passed value.
1734 : *
1735 : * @param aName The scalar name.
1736 : * @param aVal The numeric value to set the scalar to.
1737 : * @param aCx The JS context.
1738 : * @return NS_OK (always) so that the JS API call doesn't throw. In case of errors,
1739 : * a warning level message is printed in the browser console.
1740 : */
1741 : nsresult
1742 0 : TelemetryScalar::SetMaximum(const nsACString& aName, JS::HandleValue aVal, JSContext* aCx)
1743 : {
1744 : // Unpack the aVal to nsIVariant. This uses the JS context.
1745 0 : nsCOMPtr<nsIVariant> unpackedVal;
1746 : nsresult rv =
1747 0 : nsContentUtils::XPConnect()->JSToVariant(aCx, aVal, getter_AddRefs(unpackedVal));
1748 0 : if (NS_FAILED(rv)) {
1749 0 : internal_LogScalarError(aName, ScalarResult::CannotUnpackVariant);
1750 0 : return NS_OK;
1751 : }
1752 :
1753 : ScalarResult sr;
1754 : {
1755 0 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
1756 0 : sr = internal_UpdateScalar(aName, ScalarActionType::eSetMaximum, unpackedVal);
1757 : }
1758 :
1759 : // Warn the user about the error if we need to.
1760 0 : if (sr != ScalarResult::Ok) {
1761 0 : internal_LogScalarError(aName, sr);
1762 : }
1763 :
1764 0 : return NS_OK;
1765 : }
1766 :
1767 : /**
1768 : * Sets the scalar to the maximum of the current and the passed value.
1769 : *
1770 : * @param aName The scalar name.
1771 : * @param aKey The key name.
1772 : * @param aVal The numeric value to set the scalar to.
1773 : * @param aCx The JS context.
1774 : * @return NS_OK (always) so that the JS API call doesn't throw. In case of errors,
1775 : * a warning level message is printed in the browser console.
1776 : */
1777 : nsresult
1778 0 : TelemetryScalar::SetMaximum(const nsACString& aName, const nsAString& aKey, JS::HandleValue aVal,
1779 : JSContext* aCx)
1780 : {
1781 : // Unpack the aVal to nsIVariant. This uses the JS context.
1782 0 : nsCOMPtr<nsIVariant> unpackedVal;
1783 : nsresult rv =
1784 0 : nsContentUtils::XPConnect()->JSToVariant(aCx, aVal, getter_AddRefs(unpackedVal));
1785 0 : if (NS_FAILED(rv)) {
1786 0 : internal_LogScalarError(aName, ScalarResult::CannotUnpackVariant);
1787 0 : return NS_OK;
1788 : }
1789 :
1790 : ScalarResult sr;
1791 : {
1792 0 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
1793 0 : sr = internal_UpdateKeyedScalar(aName, aKey, ScalarActionType::eSetMaximum, unpackedVal);
1794 : }
1795 :
1796 : // Warn the user about the error if we need to.
1797 0 : if (sr != ScalarResult::Ok) {
1798 0 : internal_LogScalarError(aName, sr);
1799 : }
1800 :
1801 0 : return NS_OK;
1802 : }
1803 :
1804 : /**
1805 : * Sets the scalar to the maximum of the current and the passed value.
1806 : *
1807 : * @param aId The scalar enum id.
1808 : * @param aValue The numeric value to set the scalar to.
1809 : */
1810 : void
1811 0 : TelemetryScalar::SetMaximum(mozilla::Telemetry::ScalarID aId, uint32_t aValue)
1812 : {
1813 0 : if (NS_WARN_IF(!IsValidEnumId(aId))) {
1814 0 : MOZ_ASSERT_UNREACHABLE("Scalar usage requires valid ids.");
1815 : return;
1816 : }
1817 :
1818 0 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
1819 :
1820 0 : if (internal_CanRecordScalar(aId, false) != ScalarResult::Ok) {
1821 : // We can't record this scalar. Bail out.
1822 0 : return;
1823 : }
1824 :
1825 : // Accumulate in the child process if needed.
1826 0 : if (!XRE_IsParentProcess()) {
1827 : TelemetryIPCAccumulator::RecordChildScalarAction(aId, ScalarActionType::eSetMaximum,
1828 0 : ScalarVariant(aValue));
1829 0 : return;
1830 : }
1831 :
1832 0 : ScalarBase* scalar = nullptr;
1833 0 : nsresult rv = internal_GetScalarByEnum(aId, ProcessID::Parent, &scalar);
1834 0 : if (NS_FAILED(rv)) {
1835 0 : return;
1836 : }
1837 :
1838 0 : scalar->SetMaximum(aValue);
1839 : }
1840 :
1841 : /**
1842 : * Sets the keyed scalar to the maximum of the current and the passed value.
1843 : *
1844 : * @param aId The scalar enum id.
1845 : * @param aKey The key name.
1846 : * @param aValue The numeric value to set the scalar to.
1847 : */
1848 : void
1849 0 : TelemetryScalar::SetMaximum(mozilla::Telemetry::ScalarID aId, const nsAString& aKey,
1850 : uint32_t aValue)
1851 : {
1852 0 : if (NS_WARN_IF(!IsValidEnumId(aId))) {
1853 0 : MOZ_ASSERT_UNREACHABLE("Scalar usage requires valid ids.");
1854 : return;
1855 : }
1856 :
1857 0 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
1858 :
1859 0 : if (internal_CanRecordScalar(aId, true) != ScalarResult::Ok) {
1860 : // We can't record this scalar. Bail out.
1861 0 : return;
1862 : }
1863 :
1864 : // Accumulate in the child process if needed.
1865 0 : if (!XRE_IsParentProcess()) {
1866 : TelemetryIPCAccumulator::RecordChildKeyedScalarAction(
1867 0 : aId, aKey, ScalarActionType::eSetMaximum, ScalarVariant(aValue));
1868 0 : return;
1869 : }
1870 :
1871 0 : KeyedScalar* scalar = nullptr;
1872 0 : nsresult rv = internal_GetKeyedScalarByEnum(aId, ProcessID::Parent, &scalar);
1873 0 : if (NS_FAILED(rv)) {
1874 0 : return;
1875 : }
1876 :
1877 0 : scalar->SetMaximum(aKey, aValue);
1878 : }
1879 :
1880 : /**
1881 : * Serializes the scalars from the given dataset to a json-style object and resets them.
1882 : * The returned structure looks like:
1883 : * {"process": {"group1.probe":1,"group1.other_probe":false,...}, ... }.
1884 : *
1885 : * @param aDataset DATASET_RELEASE_CHANNEL_OPTOUT or DATASET_RELEASE_CHANNEL_OPTIN.
1886 : * @param aClear Whether to clear out the scalars after snapshotting.
1887 : */
1888 : nsresult
1889 0 : TelemetryScalar::CreateSnapshots(unsigned int aDataset, bool aClearScalars, JSContext* aCx,
1890 : uint8_t optional_argc, JS::MutableHandle<JS::Value> aResult)
1891 : {
1892 0 : MOZ_ASSERT(XRE_IsParentProcess(),
1893 : "Snapshotting scalars should only happen in the parent processes.");
1894 : // If no arguments were passed in, apply the default value.
1895 0 : if (!optional_argc) {
1896 0 : aClearScalars = false;
1897 : }
1898 :
1899 0 : JS::Rooted<JSObject*> root_obj(aCx, JS_NewPlainObject(aCx));
1900 0 : if (!root_obj) {
1901 0 : return NS_ERROR_FAILURE;
1902 : }
1903 0 : aResult.setObject(*root_obj);
1904 :
1905 : // Return `{}` in child processes.
1906 0 : if (!XRE_IsParentProcess()) {
1907 0 : return NS_OK;
1908 : }
1909 :
1910 : // Only lock the mutex while accessing our data, without locking any JS related code.
1911 : typedef mozilla::Pair<const char*, nsCOMPtr<nsIVariant>> DataPair;
1912 : typedef nsTArray<DataPair> ScalarArray;
1913 0 : nsDataHashtable<ProcessIDHashKey, ScalarArray> scalarsToReflect;
1914 : {
1915 0 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
1916 : // Iterate the scalars in gScalarStorageMap. The storage may contain empty or yet to be
1917 : // initialized scalars from all the supported processes.
1918 0 : for (auto iter = gScalarStorageMap.Iter(); !iter.Done(); iter.Next()) {
1919 0 : ScalarStorageMapType* scalarStorage = static_cast<ScalarStorageMapType*>(iter.Data());
1920 0 : ScalarArray& processScalars = scalarsToReflect.GetOrInsert(iter.Key());
1921 :
1922 : // Iterate each available child storage.
1923 0 : for (auto childIter = scalarStorage->Iter(); !childIter.Done(); childIter.Next()) {
1924 0 : ScalarBase* scalar = static_cast<ScalarBase*>(childIter.Data());
1925 :
1926 : // Get the informations for this scalar.
1927 0 : const ScalarInfo& info = gScalars[childIter.Key()];
1928 :
1929 : // Serialize the scalar if it's in the desired dataset.
1930 0 : if (IsInDataset(info.dataset, aDataset)) {
1931 : // Get the scalar value.
1932 0 : nsCOMPtr<nsIVariant> scalarValue;
1933 0 : nsresult rv = scalar->GetValue(scalarValue);
1934 0 : if (NS_FAILED(rv)) {
1935 0 : return rv;
1936 : }
1937 : // Append it to our list.
1938 0 : processScalars.AppendElement(mozilla::MakePair(info.name(), scalarValue));
1939 : }
1940 : }
1941 : }
1942 :
1943 0 : if (aClearScalars) {
1944 : // The map already takes care of freeing the allocated memory.
1945 0 : gScalarStorageMap.Clear();
1946 : }
1947 : }
1948 :
1949 : // Reflect it to JS.
1950 0 : for (auto iter = scalarsToReflect.Iter(); !iter.Done(); iter.Next()) {
1951 0 : ScalarArray& processScalars = iter.Data();
1952 0 : const char* processName = GetNameForProcessID(ProcessID(iter.Key()));
1953 :
1954 : // Create the object that will hold the scalars for this process and add it
1955 : // to the returned root object.
1956 0 : JS::RootedObject processObj(aCx, JS_NewPlainObject(aCx));
1957 0 : if (!processObj ||
1958 0 : !JS_DefineProperty(aCx, root_obj, processName, processObj, JSPROP_ENUMERATE)) {
1959 0 : return NS_ERROR_FAILURE;
1960 : }
1961 :
1962 0 : for (nsTArray<DataPair>::size_type i = 0; i < processScalars.Length(); i++) {
1963 0 : const DataPair& scalar = processScalars[i];
1964 :
1965 : // Convert it to a JS Val.
1966 0 : JS::Rooted<JS::Value> scalarJsValue(aCx);
1967 : nsresult rv =
1968 0 : nsContentUtils::XPConnect()->VariantToJS(aCx, processObj, scalar.second(), &scalarJsValue);
1969 0 : if (NS_FAILED(rv)) {
1970 0 : return rv;
1971 : }
1972 :
1973 : // Add it to the scalar object.
1974 0 : if (!JS_DefineProperty(aCx, processObj, scalar.first(), scalarJsValue, JSPROP_ENUMERATE)) {
1975 0 : return NS_ERROR_FAILURE;
1976 : }
1977 : }
1978 : }
1979 :
1980 0 : return NS_OK;
1981 : }
1982 :
1983 : /**
1984 : * Serializes the scalars from the given dataset to a json-style object and resets them.
1985 : * The returned structure looks like:
1986 : * { "process": { "group1.probe": { "key_1": 2, "key_2": 1, ... }, ... }, ... }
1987 : *
1988 : * @param aDataset DATASET_RELEASE_CHANNEL_OPTOUT or DATASET_RELEASE_CHANNEL_OPTIN.
1989 : * @param aClear Whether to clear out the keyed scalars after snapshotting.
1990 : */
1991 : nsresult
1992 0 : TelemetryScalar::CreateKeyedSnapshots(unsigned int aDataset, bool aClearScalars, JSContext* aCx,
1993 : uint8_t optional_argc, JS::MutableHandle<JS::Value> aResult)
1994 : {
1995 0 : MOZ_ASSERT(XRE_IsParentProcess(),
1996 : "Snapshotting scalars should only happen in the parent processes.");
1997 : // If no arguments were passed in, apply the default value.
1998 0 : if (!optional_argc) {
1999 0 : aClearScalars = false;
2000 : }
2001 :
2002 0 : JS::Rooted<JSObject*> root_obj(aCx, JS_NewPlainObject(aCx));
2003 0 : if (!root_obj) {
2004 0 : return NS_ERROR_FAILURE;
2005 : }
2006 0 : aResult.setObject(*root_obj);
2007 :
2008 : // Return `{}` in child processes.
2009 0 : if (!XRE_IsParentProcess()) {
2010 0 : return NS_OK;
2011 : }
2012 :
2013 : // Only lock the mutex while accessing our data, without locking any JS related code.
2014 : typedef mozilla::Pair<const char*, nsTArray<KeyedScalar::KeyValuePair>> DataPair;
2015 : typedef nsTArray<DataPair> ScalarArray;
2016 0 : nsDataHashtable<ProcessIDHashKey, ScalarArray> scalarsToReflect;
2017 : {
2018 0 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
2019 : // Iterate the scalars in gKeyedScalarStorageMap. The storage may contain empty or yet
2020 : // to be initialized scalars from all the supported processes.
2021 0 : for (auto iter = gKeyedScalarStorageMap.Iter(); !iter.Done(); iter.Next()) {
2022 : KeyedScalarStorageMapType* scalarStorage =
2023 0 : static_cast<KeyedScalarStorageMapType*>(iter.Data());
2024 0 : ScalarArray& processScalars = scalarsToReflect.GetOrInsert(iter.Key());
2025 :
2026 0 : for (auto childIter = scalarStorage->Iter(); !childIter.Done(); childIter.Next()) {
2027 0 : KeyedScalar* scalar = static_cast<KeyedScalar*>(childIter.Data());
2028 :
2029 : // Get the informations for this scalar.
2030 0 : const ScalarInfo& info = gScalars[childIter.Key()];
2031 :
2032 : // Serialize the scalar if it's in the desired dataset.
2033 0 : if (IsInDataset(info.dataset, aDataset)) {
2034 : // Get the keys for this scalar.
2035 0 : nsTArray<KeyedScalar::KeyValuePair> scalarKeyedData;
2036 0 : nsresult rv = scalar->GetValue(scalarKeyedData);
2037 0 : if (NS_FAILED(rv)) {
2038 0 : return rv;
2039 : }
2040 : // Append it to our list.
2041 0 : processScalars.AppendElement(mozilla::MakePair(info.name(), scalarKeyedData));
2042 : }
2043 : }
2044 : }
2045 :
2046 0 : if (aClearScalars) {
2047 : // The map already takes care of freeing the allocated memory.
2048 0 : gKeyedScalarStorageMap.Clear();
2049 : }
2050 : }
2051 :
2052 : // Reflect it to JS.
2053 0 : for (auto iter = scalarsToReflect.Iter(); !iter.Done(); iter.Next()) {
2054 0 : ScalarArray& processScalars = iter.Data();
2055 0 : const char* processName = GetNameForProcessID(ProcessID(iter.Key()));
2056 :
2057 : // Create the object that will hold the scalars for this process and add it
2058 : // to the returned root object.
2059 0 : JS::RootedObject processObj(aCx, JS_NewPlainObject(aCx));
2060 0 : if (!processObj ||
2061 0 : !JS_DefineProperty(aCx, root_obj, processName, processObj, JSPROP_ENUMERATE)) {
2062 0 : return NS_ERROR_FAILURE;
2063 : }
2064 :
2065 0 : for (nsTArray<DataPair>::size_type i = 0; i < processScalars.Length(); i++) {
2066 0 : const DataPair& keyedScalarData = processScalars[i];
2067 :
2068 : // Go through each keyed scalar and create a keyed scalar object.
2069 : // This object will hold the values for all the keyed scalar keys.
2070 0 : JS::RootedObject keyedScalarObj(aCx, JS_NewPlainObject(aCx));
2071 :
2072 : // Define a property for each scalar key, then add it to the keyed scalar
2073 : // object.
2074 0 : const nsTArray<KeyedScalar::KeyValuePair>& keyProps = keyedScalarData.second();
2075 0 : for (uint32_t i = 0; i < keyProps.Length(); i++) {
2076 0 : const KeyedScalar::KeyValuePair& keyData = keyProps[i];
2077 :
2078 : // Convert the value for the key to a JSValue.
2079 0 : JS::Rooted<JS::Value> keyJsValue(aCx);
2080 : nsresult rv =
2081 0 : nsContentUtils::XPConnect()->VariantToJS(aCx, keyedScalarObj, keyData.second(), &keyJsValue);
2082 0 : if (NS_FAILED(rv)) {
2083 0 : return rv;
2084 : }
2085 :
2086 : // Add the key to the scalar representation.
2087 0 : const NS_ConvertUTF8toUTF16 key(keyData.first());
2088 0 : if (!JS_DefineUCProperty(aCx, keyedScalarObj, key.Data(), key.Length(), keyJsValue, JSPROP_ENUMERATE)) {
2089 0 : return NS_ERROR_FAILURE;
2090 : }
2091 : }
2092 :
2093 : // Add the scalar to the root object.
2094 0 : if (!JS_DefineProperty(aCx, processObj, keyedScalarData.first(), keyedScalarObj, JSPROP_ENUMERATE)) {
2095 0 : return NS_ERROR_FAILURE;
2096 : }
2097 : }
2098 : }
2099 :
2100 0 : return NS_OK;
2101 : }
2102 :
2103 : /**
2104 : * Resets all the stored scalars. This is intended to be only used in tests.
2105 : */
2106 : void
2107 0 : TelemetryScalar::ClearScalars()
2108 : {
2109 0 : MOZ_ASSERT(XRE_IsParentProcess(), "Scalars should only be cleared in the parent process.");
2110 0 : if (!XRE_IsParentProcess()) {
2111 0 : return;
2112 : }
2113 :
2114 0 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
2115 0 : gScalarStorageMap.Clear();
2116 0 : gKeyedScalarStorageMap.Clear();
2117 : }
2118 :
2119 : size_t
2120 0 : TelemetryScalar::GetMapShallowSizesOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
2121 : {
2122 0 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
2123 0 : return gScalarNameIDMap.ShallowSizeOfExcludingThis(aMallocSizeOf);
2124 : }
2125 :
2126 : size_t
2127 0 : TelemetryScalar::GetScalarSizesOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
2128 : {
2129 0 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
2130 0 : size_t n = 0;
2131 : // Account for scalar data coming from parent and child processes.
2132 0 : for (auto iter = gScalarStorageMap.Iter(); !iter.Done(); iter.Next()) {
2133 0 : ScalarStorageMapType* scalarStorage = static_cast<ScalarStorageMapType*>(iter.Data());
2134 0 : for (auto childIter = scalarStorage->Iter(); !childIter.Done(); childIter.Next()) {
2135 0 : ScalarBase* scalar = static_cast<ScalarBase*>(childIter.Data());
2136 0 : n += scalar->SizeOfIncludingThis(aMallocSizeOf);
2137 : }
2138 : }
2139 : // Also account for keyed scalar data coming from parent and child processes.
2140 0 : for (auto iter = gKeyedScalarStorageMap.Iter(); !iter.Done(); iter.Next()) {
2141 : KeyedScalarStorageMapType* scalarStorage =
2142 0 : static_cast<KeyedScalarStorageMapType*>(iter.Data());
2143 0 : for (auto childIter = scalarStorage->Iter(); !childIter.Done(); childIter.Next()) {
2144 0 : KeyedScalar* scalar = static_cast<KeyedScalar*>(childIter.Data());
2145 0 : n += scalar->SizeOfIncludingThis(aMallocSizeOf);
2146 : }
2147 : }
2148 0 : return n;
2149 : }
2150 :
2151 : void
2152 0 : TelemetryScalar::UpdateChildData(ProcessID aProcessType,
2153 : const nsTArray<mozilla::Telemetry::ScalarAction>& aScalarActions)
2154 : {
2155 0 : MOZ_ASSERT(XRE_IsParentProcess(),
2156 : "The stored child processes scalar data must be updated from the parent process.");
2157 0 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
2158 0 : if (!internal_CanRecordBase()) {
2159 0 : return;
2160 : }
2161 :
2162 0 : for (auto& upd : aScalarActions) {
2163 0 : if (NS_WARN_IF(!IsValidEnumId(upd.mId))) {
2164 0 : MOZ_ASSERT_UNREACHABLE("Scalar usage requires valid ids.");
2165 0 : continue;
2166 : }
2167 :
2168 0 : if (internal_IsKeyedScalar(upd.mId)) {
2169 0 : continue;
2170 : }
2171 :
2172 : // Are we allowed to record this scalar? We don't need to check for
2173 : // allowed processes here, that's taken care of when recording
2174 : // in child processes.
2175 0 : if (!internal_CanRecordForScalarID(upd.mId)) {
2176 0 : continue;
2177 : }
2178 :
2179 : // Refresh the data in the parent process with the data coming from the child
2180 : // processes.
2181 0 : ScalarBase* scalar = nullptr;
2182 0 : nsresult rv = internal_GetScalarByEnum(upd.mId, aProcessType, &scalar);
2183 0 : if (NS_FAILED(rv)) {
2184 0 : NS_WARNING("NS_FAILED internal_GetScalarByEnum for CHILD");
2185 0 : continue;
2186 : }
2187 :
2188 0 : if (upd.mData.isNothing()) {
2189 0 : MOZ_ASSERT(false, "There is no data in the ScalarActionType.");
2190 : continue;
2191 : }
2192 :
2193 : // Get the type of this scalar from the scalar ID. We already checked
2194 : // for its validity a few lines above.
2195 0 : const uint32_t scalarType = gScalars[static_cast<uint32_t>(upd.mId)].kind;
2196 :
2197 : // Extract the data from the mozilla::Variant.
2198 0 : switch (upd.mActionType)
2199 : {
2200 : case ScalarActionType::eSet:
2201 : {
2202 0 : switch (scalarType)
2203 : {
2204 : case nsITelemetry::SCALAR_COUNT:
2205 0 : scalar->SetValue(upd.mData->as<uint32_t>());
2206 0 : break;
2207 : case nsITelemetry::SCALAR_BOOLEAN:
2208 0 : scalar->SetValue(upd.mData->as<bool>());
2209 0 : break;
2210 : case nsITelemetry::SCALAR_STRING:
2211 0 : scalar->SetValue(upd.mData->as<nsString>());
2212 0 : break;
2213 : }
2214 0 : break;
2215 : }
2216 : case ScalarActionType::eAdd:
2217 : {
2218 0 : if (scalarType != nsITelemetry::SCALAR_COUNT) {
2219 0 : NS_WARNING("Attempting to add on a non count scalar.");
2220 0 : continue;
2221 : }
2222 : // We only support adding uint32_t.
2223 0 : scalar->AddValue(upd.mData->as<uint32_t>());
2224 0 : break;
2225 : }
2226 : case ScalarActionType::eSetMaximum:
2227 : {
2228 0 : if (scalarType != nsITelemetry::SCALAR_COUNT) {
2229 0 : NS_WARNING("Attempting to add on a non count scalar.");
2230 0 : continue;
2231 : }
2232 : // We only support SetMaximum on uint32_t.
2233 0 : scalar->SetMaximum(upd.mData->as<uint32_t>());
2234 0 : break;
2235 : }
2236 : default:
2237 0 : NS_WARNING("Unsupported action coming from scalar child updates.");
2238 : }
2239 : }
2240 : }
2241 :
2242 : void
2243 0 : TelemetryScalar::UpdateChildKeyedData(ProcessID aProcessType,
2244 : const nsTArray<mozilla::Telemetry::KeyedScalarAction>& aScalarActions)
2245 : {
2246 0 : MOZ_ASSERT(XRE_IsParentProcess(),
2247 : "The stored child processes keyed scalar data must be updated from the parent process.");
2248 0 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
2249 0 : if (!internal_CanRecordBase()) {
2250 0 : return;
2251 : }
2252 :
2253 0 : for (auto& upd : aScalarActions) {
2254 0 : if (NS_WARN_IF(!IsValidEnumId(upd.mId))) {
2255 0 : MOZ_ASSERT_UNREACHABLE("Scalar usage requires valid ids.");
2256 0 : continue;
2257 : }
2258 :
2259 0 : if (!internal_IsKeyedScalar(upd.mId)) {
2260 0 : continue;
2261 : }
2262 :
2263 : // Are we allowed to record this scalar? We don't need to check for
2264 : // allowed processes here, that's taken care of when recording
2265 : // in child processes.
2266 0 : if (!internal_CanRecordForScalarID(upd.mId)) {
2267 0 : continue;
2268 : }
2269 :
2270 : // Refresh the data in the parent process with the data coming from the child
2271 : // processes.
2272 0 : KeyedScalar* scalar = nullptr;
2273 0 : nsresult rv = internal_GetKeyedScalarByEnum(upd.mId, aProcessType, &scalar);
2274 0 : if (NS_FAILED(rv)) {
2275 0 : NS_WARNING("NS_FAILED internal_GetScalarByEnum for CHILD");
2276 0 : continue;
2277 : }
2278 :
2279 0 : if (upd.mData.isNothing()) {
2280 0 : MOZ_ASSERT(false, "There is no data in the KeyedScalarAction.");
2281 : continue;
2282 : }
2283 :
2284 : // Get the type of this scalar from the scalar ID. We already checked
2285 : // for its validity a few lines above.
2286 0 : const uint32_t scalarType = gScalars[static_cast<uint32_t>(upd.mId)].kind;
2287 :
2288 : // Extract the data from the mozilla::Variant.
2289 0 : switch (upd.mActionType)
2290 : {
2291 : case ScalarActionType::eSet:
2292 : {
2293 0 : switch (scalarType)
2294 : {
2295 : case nsITelemetry::SCALAR_COUNT:
2296 0 : scalar->SetValue(NS_ConvertUTF8toUTF16(upd.mKey), upd.mData->as<uint32_t>());
2297 0 : break;
2298 : case nsITelemetry::SCALAR_BOOLEAN:
2299 0 : scalar->SetValue(NS_ConvertUTF8toUTF16(upd.mKey), upd.mData->as<bool>());
2300 0 : break;
2301 : default:
2302 0 : NS_WARNING("Unsupported type coming from scalar child updates.");
2303 : }
2304 0 : break;
2305 : }
2306 : case ScalarActionType::eAdd:
2307 : {
2308 0 : if (scalarType != nsITelemetry::SCALAR_COUNT) {
2309 0 : NS_WARNING("Attempting to add on a non count scalar.");
2310 0 : continue;
2311 : }
2312 : // We only support adding on uint32_t.
2313 0 : scalar->AddValue(NS_ConvertUTF8toUTF16(upd.mKey), upd.mData->as<uint32_t>());
2314 0 : break;
2315 : }
2316 : case ScalarActionType::eSetMaximum:
2317 : {
2318 0 : if (scalarType != nsITelemetry::SCALAR_COUNT) {
2319 0 : NS_WARNING("Attempting to add on a non count scalar.");
2320 0 : continue;
2321 : }
2322 : // We only support SetMaximum on uint32_t.
2323 0 : scalar->SetMaximum(NS_ConvertUTF8toUTF16(upd.mKey), upd.mData->as<uint32_t>());
2324 0 : break;
2325 : }
2326 : default:
2327 0 : NS_WARNING("Unsupported action coming from keyed scalar child updates.");
2328 : }
2329 : }
2330 : }
2331 :
2332 : void
2333 2 : TelemetryScalar::RecordDiscardedData(ProcessID aProcessType,
2334 : const mozilla::Telemetry::DiscardedData& aDiscardedData)
2335 : {
2336 2 : MOZ_ASSERT(XRE_IsParentProcess(),
2337 : "Discarded Data must be updated from the parent process.");
2338 4 : StaticMutexAutoLock locker(gTelemetryScalarsMutex);
2339 2 : if (!internal_CanRecordBase()) {
2340 0 : return;
2341 : }
2342 :
2343 2 : ScalarBase* scalar = nullptr;
2344 4 : mozilla::DebugOnly<nsresult> rv;
2345 :
2346 4 : rv = internal_GetScalarByEnum(ScalarID::TELEMETRY_DISCARDED_ACCUMULATIONS,
2347 2 : aProcessType, &scalar);
2348 2 : MOZ_ASSERT(NS_SUCCEEDED(rv));
2349 2 : scalar->AddValue(aDiscardedData.mDiscardedHistogramAccumulations);
2350 :
2351 4 : rv = internal_GetScalarByEnum(ScalarID::TELEMETRY_DISCARDED_KEYED_ACCUMULATIONS,
2352 2 : aProcessType, &scalar);
2353 2 : MOZ_ASSERT(NS_SUCCEEDED(rv));
2354 2 : scalar->AddValue(aDiscardedData.mDiscardedKeyedHistogramAccumulations);
2355 :
2356 4 : rv = internal_GetScalarByEnum(ScalarID::TELEMETRY_DISCARDED_SCALAR_ACTIONS,
2357 2 : aProcessType, &scalar);
2358 2 : MOZ_ASSERT(NS_SUCCEEDED(rv));
2359 2 : scalar->AddValue(aDiscardedData.mDiscardedScalarActions);
2360 :
2361 4 : rv = internal_GetScalarByEnum(ScalarID::TELEMETRY_DISCARDED_KEYED_SCALAR_ACTIONS,
2362 2 : aProcessType, &scalar);
2363 2 : MOZ_ASSERT(NS_SUCCEEDED(rv));
2364 2 : scalar->AddValue(aDiscardedData.mDiscardedKeyedScalarActions);
2365 :
2366 4 : rv = internal_GetScalarByEnum(ScalarID::TELEMETRY_DISCARDED_CHILD_EVENTS,
2367 2 : aProcessType, &scalar);
2368 2 : MOZ_ASSERT(NS_SUCCEEDED(rv));
2369 2 : scalar->AddValue(aDiscardedData.mDiscardedChildEvents);
2370 9 : }
|