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 "jsapi.h"
8 : #include "jsfriendapi.h"
9 : #include "js/GCAPI.h"
10 : #include "nsString.h"
11 : #include "nsTHashtable.h"
12 : #include "nsHashKeys.h"
13 : #include "nsBaseHashtable.h"
14 : #include "nsClassHashtable.h"
15 : #include "nsITelemetry.h"
16 :
17 : #include "mozilla/dom/ToJSValue.h"
18 : #include "mozilla/gfx/GPUProcessManager.h"
19 : #include "mozilla/Atomics.h"
20 : #include "mozilla/StartupTimeline.h"
21 : #include "mozilla/StaticMutex.h"
22 : #include "mozilla/Unused.h"
23 :
24 : #include "TelemetryCommon.h"
25 : #include "TelemetryHistogram.h"
26 : #include "ipc/TelemetryIPCAccumulator.h"
27 :
28 : #include "base/histogram.h"
29 :
30 : using base::Histogram;
31 : using base::StatisticsRecorder;
32 : using base::BooleanHistogram;
33 : using base::CountHistogram;
34 : using base::FlagHistogram;
35 : using base::LinearHistogram;
36 : using mozilla::StaticMutex;
37 : using mozilla::StaticMutexAutoLock;
38 : using mozilla::Telemetry::Accumulation;
39 : using mozilla::Telemetry::KeyedAccumulation;
40 : using mozilla::Telemetry::ProcessID;
41 : using mozilla::Telemetry::Common::LogToBrowserConsole;
42 : using mozilla::Telemetry::Common::RecordedProcessType;
43 :
44 : namespace TelemetryIPCAccumulator = mozilla::TelemetryIPCAccumulator;
45 :
46 : ////////////////////////////////////////////////////////////////////////
47 : ////////////////////////////////////////////////////////////////////////
48 : //
49 : // Naming: there are two kinds of functions in this file:
50 : //
51 : // * Functions named internal_*: these can only be reached via an
52 : // interface function (TelemetryHistogram::*). They mostly expect
53 : // the interface function to have acquired
54 : // |gTelemetryHistogramMutex|, so they do not have to be
55 : // thread-safe. However, those internal_* functions that are
56 : // reachable from internal_WrapAndReturnHistogram and
57 : // internal_WrapAndReturnKeyedHistogram can sometimes be called
58 : // without |gTelemetryHistogramMutex|, and so might be racey.
59 : //
60 : // * Functions named TelemetryHistogram::*. This is the external interface.
61 : // Entries and exits to these functions are serialised using
62 : // |gTelemetryHistogramMutex|, except for GetKeyedHistogramSnapshots and
63 : // CreateHistogramSnapshots.
64 : //
65 : // Avoiding races and deadlocks:
66 : //
67 : // All functions in the external interface (TelemetryHistogram::*) are
68 : // serialised using the mutex |gTelemetryHistogramMutex|. This means
69 : // that the external interface is thread-safe, and many of the
70 : // internal_* functions can ignore thread safety. But it also brings
71 : // a danger of deadlock if any function in the external interface can
72 : // get back to that interface. That is, we will deadlock on any call
73 : // chain like this
74 : //
75 : // TelemetryHistogram::* -> .. any functions .. -> TelemetryHistogram::*
76 : //
77 : // To reduce the danger of that happening, observe the following rules:
78 : //
79 : // * No function in TelemetryHistogram::* may directly call, nor take the
80 : // address of, any other function in TelemetryHistogram::*.
81 : //
82 : // * No internal function internal_* may call, nor take the address
83 : // of, any function in TelemetryHistogram::*.
84 : //
85 : // internal_WrapAndReturnHistogram and
86 : // internal_WrapAndReturnKeyedHistogram are not protected by
87 : // |gTelemetryHistogramMutex| because they make calls to the JS
88 : // engine, but that can in turn call back to Telemetry and hence back
89 : // to a TelemetryHistogram:: function, in order to report GC and other
90 : // statistics. This would lead to deadlock due to attempted double
91 : // acquisition of |gTelemetryHistogramMutex|, if the internal_* functions
92 : // were required to be protected by |gTelemetryHistogramMutex|. To
93 : // break that cycle, we relax that requirement. Unfortunately this
94 : // means that this file is not guaranteed race-free.
95 :
96 :
97 : ////////////////////////////////////////////////////////////////////////
98 : ////////////////////////////////////////////////////////////////////////
99 : //
100 : // PRIVATE TYPES
101 :
102 : #define EXPIRED_ID "__expired__"
103 : #define SUBSESSION_HISTOGRAM_PREFIX "sub#"
104 : #define KEYED_HISTOGRAM_NAME_SEPARATOR "#"
105 : #define CONTENT_HISTOGRAM_SUFFIX "#content"
106 : #define GPU_HISTOGRAM_SUFFIX "#gpu"
107 : #define EXTENSION_HISTOGRAM_SUFFIX "#extension"
108 :
109 : namespace {
110 :
111 : using mozilla::Telemetry::Common::AutoHashtable;
112 : using mozilla::Telemetry::Common::IsExpiredVersion;
113 : using mozilla::Telemetry::Common::CanRecordDataset;
114 : using mozilla::Telemetry::Common::IsInDataset;
115 :
116 : class KeyedHistogram;
117 :
118 : typedef nsBaseHashtableET<nsDepCharHashKey, mozilla::Telemetry::HistogramID>
119 : CharPtrEntryType;
120 :
121 : typedef AutoHashtable<CharPtrEntryType> HistogramMapType;
122 :
123 : typedef nsClassHashtable<nsCStringHashKey, KeyedHistogram>
124 : KeyedHistogramMapType;
125 :
126 : // Hardcoded probes
127 : struct HistogramInfo {
128 : uint32_t min;
129 : uint32_t max;
130 : uint32_t bucketCount;
131 : uint32_t histogramType;
132 : uint32_t id_offset;
133 : uint32_t expiration_offset;
134 : uint32_t dataset;
135 : uint32_t label_index;
136 : uint32_t label_count;
137 : RecordedProcessType record_in_processes;
138 : bool keyed;
139 :
140 : const char *id() const;
141 : const char *expiration() const;
142 : nsresult label_id(const char* label, uint32_t* labelId) const;
143 : };
144 :
145 : enum reflectStatus {
146 : REFLECT_OK,
147 : REFLECT_CORRUPT,
148 : REFLECT_FAILURE
149 : };
150 :
151 : typedef StatisticsRecorder::Histograms::iterator HistogramIterator;
152 :
153 : } // namespace
154 :
155 :
156 : ////////////////////////////////////////////////////////////////////////
157 : ////////////////////////////////////////////////////////////////////////
158 : //
159 : // PRIVATE STATE, SHARED BY ALL THREADS
160 :
161 : namespace {
162 :
163 : // Set to true once this global state has been initialized
164 : bool gInitDone = false;
165 :
166 : bool gCanRecordBase = false;
167 : bool gCanRecordExtended = false;
168 :
169 3 : HistogramMapType gHistogramMap(mozilla::Telemetry::HistogramCount);
170 :
171 3 : KeyedHistogramMapType gKeyedHistograms;
172 :
173 : bool gCorruptHistograms[mozilla::Telemetry::HistogramCount];
174 :
175 : // This is for gHistograms, gHistogramStringTable
176 : #include "TelemetryHistogramData.inc"
177 :
178 : // The singleton StatisticsRecorder object for this process.
179 : base::StatisticsRecorder* gStatisticsRecorder = nullptr;
180 :
181 : } // namespace
182 :
183 :
184 : ////////////////////////////////////////////////////////////////////////
185 : ////////////////////////////////////////////////////////////////////////
186 : //
187 : // PRIVATE CONSTANTS
188 :
189 : namespace {
190 :
191 : // List of histogram IDs which should have recording disabled initially.
192 : const mozilla::Telemetry::HistogramID kRecordingInitiallyDisabledIDs[] = {
193 : mozilla::Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS,
194 :
195 : // The array must not be empty. Leave these item here.
196 : mozilla::Telemetry::TELEMETRY_TEST_COUNT_INIT_NO_RECORD,
197 : mozilla::Telemetry::TELEMETRY_TEST_KEYED_COUNT_INIT_NO_RECORD
198 : };
199 :
200 : } // namespace
201 :
202 :
203 : ////////////////////////////////////////////////////////////////////////
204 : ////////////////////////////////////////////////////////////////////////
205 : //
206 : // PRIVATE: Misc small helpers
207 :
208 : namespace {
209 :
210 : bool
211 8440 : internal_CanRecordBase() {
212 8440 : return gCanRecordBase;
213 : }
214 :
215 : bool
216 3661 : internal_CanRecordExtended() {
217 3661 : return gCanRecordExtended;
218 : }
219 :
220 : bool
221 4775 : internal_IsHistogramEnumId(mozilla::Telemetry::HistogramID aID)
222 : {
223 : static_assert(((mozilla::Telemetry::HistogramID)-1 > 0), "ID should be unsigned.");
224 4775 : return aID < mozilla::Telemetry::HistogramCount;
225 : }
226 :
227 : // Note: this is completely unrelated to mozilla::IsEmpty.
228 : bool
229 0 : internal_IsEmpty(const Histogram *h)
230 : {
231 0 : Histogram::SampleSet ss;
232 0 : h->SnapshotSample(&ss);
233 0 : return ss.counts(0) == 0 && ss.sum() == 0;
234 : }
235 :
236 : bool
237 0 : internal_IsExpired(const Histogram *histogram)
238 : {
239 0 : return histogram->histogram_name() == EXPIRED_ID;
240 : }
241 :
242 : nsresult
243 0 : internal_GetRegisteredHistogramIds(bool keyed, uint32_t dataset,
244 : uint32_t *aCount, char*** aHistograms)
245 : {
246 0 : nsTArray<char*> collection;
247 :
248 0 : for (const auto & h : gHistograms) {
249 0 : if (IsExpiredVersion(h.expiration()) ||
250 0 : h.keyed != keyed ||
251 0 : !IsInDataset(h.dataset, dataset)) {
252 0 : continue;
253 : }
254 :
255 0 : const char* id = h.id();
256 0 : const size_t len = strlen(id);
257 0 : collection.AppendElement(static_cast<char*>(nsMemory::Clone(id, len+1)));
258 : }
259 :
260 0 : const size_t bytes = collection.Length() * sizeof(char*);
261 0 : char** histograms = static_cast<char**>(moz_xmalloc(bytes));
262 0 : memcpy(histograms, collection.Elements(), bytes);
263 0 : *aHistograms = histograms;
264 0 : *aCount = collection.Length();
265 :
266 0 : return NS_OK;
267 : }
268 :
269 : const char *
270 12272 : HistogramInfo::id() const
271 : {
272 12272 : return &gHistogramStringTable[this->id_offset];
273 : }
274 :
275 : const char *
276 10066 : HistogramInfo::expiration() const
277 : {
278 10066 : return &gHistogramStringTable[this->expiration_offset];
279 : }
280 :
281 : nsresult
282 0 : HistogramInfo::label_id(const char* label, uint32_t* labelId) const
283 : {
284 0 : MOZ_ASSERT(label);
285 0 : MOZ_ASSERT(this->histogramType == nsITelemetry::HISTOGRAM_CATEGORICAL);
286 0 : if (this->histogramType != nsITelemetry::HISTOGRAM_CATEGORICAL) {
287 0 : return NS_ERROR_FAILURE;
288 : }
289 :
290 0 : for (uint32_t i = 0; i < this->label_count; ++i) {
291 : // gHistogramLabelTable contains the indices of the label strings in the
292 : // gHistogramStringTable.
293 : // They are stored in-order and consecutively, from the offset label_index
294 : // to (label_index + label_count).
295 0 : uint32_t string_offset = gHistogramLabelTable[this->label_index + i];
296 0 : const char* const str = &gHistogramStringTable[string_offset];
297 0 : if (::strcmp(label, str) == 0) {
298 0 : *labelId = i;
299 0 : return NS_OK;
300 : }
301 : }
302 :
303 0 : return NS_ERROR_FAILURE;
304 : }
305 :
306 : } // namespace
307 :
308 :
309 : ////////////////////////////////////////////////////////////////////////
310 : ////////////////////////////////////////////////////////////////////////
311 : //
312 : // PRIVATE: Histogram Get, Add, Clone, Clear functions
313 :
314 : namespace {
315 :
316 : nsresult
317 4854 : internal_CheckHistogramArguments(uint32_t histogramType,
318 : uint32_t min, uint32_t max,
319 : uint32_t bucketCount, bool haveOptArgs)
320 : {
321 4854 : if (histogramType != nsITelemetry::HISTOGRAM_BOOLEAN
322 3867 : && histogramType != nsITelemetry::HISTOGRAM_FLAG
323 3711 : && histogramType != nsITelemetry::HISTOGRAM_COUNT) {
324 : // The min, max & bucketCount arguments are not optional for this type.
325 3182 : if (!haveOptArgs)
326 0 : return NS_ERROR_ILLEGAL_VALUE;
327 :
328 : // Sanity checks for histogram parameters.
329 3182 : if (min >= max)
330 0 : return NS_ERROR_ILLEGAL_VALUE;
331 :
332 3182 : if (bucketCount <= 2)
333 0 : return NS_ERROR_ILLEGAL_VALUE;
334 :
335 3182 : if (min < 1)
336 0 : return NS_ERROR_ILLEGAL_VALUE;
337 : }
338 :
339 4854 : return NS_OK;
340 : }
341 :
342 : /*
343 : * min, max & bucketCount are optional for boolean, flag & count histograms.
344 : * haveOptArgs has to be set if the caller provides them.
345 : */
346 : nsresult
347 4854 : internal_HistogramGet(const char *name, const char *expiration,
348 : uint32_t histogramType, uint32_t min, uint32_t max,
349 : uint32_t bucketCount, bool haveOptArgs,
350 : Histogram **result)
351 : {
352 4854 : nsresult rv = internal_CheckHistogramArguments(histogramType, min, max,
353 4854 : bucketCount, haveOptArgs);
354 4854 : if (NS_FAILED(rv)) {
355 0 : return rv;
356 : }
357 :
358 4854 : if (IsExpiredVersion(expiration)) {
359 532 : name = EXPIRED_ID;
360 532 : min = 1;
361 532 : max = 2;
362 532 : bucketCount = 3;
363 532 : histogramType = nsITelemetry::HISTOGRAM_LINEAR;
364 : }
365 :
366 4854 : switch (histogramType) {
367 : case nsITelemetry::HISTOGRAM_EXPONENTIAL:
368 1862 : *result = Histogram::FactoryGet(name, min, max, bucketCount, Histogram::kUmaTargetedHistogramFlag);
369 1862 : break;
370 : case nsITelemetry::HISTOGRAM_LINEAR:
371 : case nsITelemetry::HISTOGRAM_CATEGORICAL:
372 1567 : *result = LinearHistogram::FactoryGet(name, min, max, bucketCount, Histogram::kUmaTargetedHistogramFlag);
373 1567 : break;
374 : case nsITelemetry::HISTOGRAM_BOOLEAN:
375 930 : *result = BooleanHistogram::FactoryGet(name, Histogram::kUmaTargetedHistogramFlag);
376 930 : break;
377 : case nsITelemetry::HISTOGRAM_FLAG:
378 141 : *result = FlagHistogram::FactoryGet(name, Histogram::kUmaTargetedHistogramFlag);
379 141 : break;
380 : case nsITelemetry::HISTOGRAM_COUNT:
381 354 : *result = CountHistogram::FactoryGet(name, Histogram::kUmaTargetedHistogramFlag);
382 354 : break;
383 : default:
384 0 : NS_ASSERTION(false, "Invalid histogram type");
385 0 : return NS_ERROR_INVALID_ARG;
386 : }
387 4854 : return NS_OK;
388 : }
389 :
390 : // Read the process type from the given histogram name. The process type, if
391 : // one exists, is embedded in a suffix.
392 : mozilla::Telemetry::ProcessID
393 183 : GetProcessFromName(const nsACString& aString)
394 : {
395 183 : if (StringEndsWith(aString, NS_LITERAL_CSTRING(CONTENT_HISTOGRAM_SUFFIX))) {
396 6 : return ProcessID::Content;
397 : }
398 177 : if (StringEndsWith(aString, NS_LITERAL_CSTRING(GPU_HISTOGRAM_SUFFIX))) {
399 0 : return ProcessID::Gpu;
400 : }
401 177 : if (StringEndsWith(aString, NS_LITERAL_CSTRING(EXTENSION_HISTOGRAM_SUFFIX))) {
402 0 : return ProcessID::Extension;
403 : }
404 177 : return ProcessID::Parent;
405 : }
406 :
407 : const char*
408 5116 : SuffixForProcessType(mozilla::Telemetry::ProcessID aProcessType)
409 : {
410 5116 : switch (aProcessType) {
411 : case ProcessID::Parent:
412 4910 : return nullptr;
413 : case ProcessID::Content:
414 206 : return CONTENT_HISTOGRAM_SUFFIX;
415 : case ProcessID::Gpu:
416 0 : return GPU_HISTOGRAM_SUFFIX;
417 : case ProcessID::Extension:
418 0 : return EXTENSION_HISTOGRAM_SUFFIX;
419 : default:
420 0 : MOZ_ASSERT_UNREACHABLE("unknown process type");
421 : return nullptr;
422 : }
423 : }
424 :
425 : CharPtrEntryType*
426 104 : internal_GetHistogramMapEntry(const char* aName)
427 : {
428 208 : nsDependentCString name(aName);
429 104 : ProcessID process = GetProcessFromName(name);
430 104 : const char* suffix = SuffixForProcessType(process);
431 104 : if (!suffix) {
432 101 : return gHistogramMap.GetEntry(aName);
433 : }
434 :
435 6 : auto root = Substring(name, 0, name.Length() - strlen(suffix));
436 3 : return gHistogramMap.GetEntry(PromiseFlatCString(root).get());
437 : }
438 :
439 : nsresult
440 104 : internal_GetHistogramEnumId(const char *name, mozilla::Telemetry::HistogramID *id)
441 : {
442 104 : if (!gInitDone) {
443 0 : return NS_ERROR_FAILURE;
444 : }
445 :
446 104 : CharPtrEntryType *entry = internal_GetHistogramMapEntry(name);
447 104 : if (!entry) {
448 10 : return NS_ERROR_INVALID_ARG;
449 : }
450 94 : *id = entry->mData;
451 94 : return NS_OK;
452 : }
453 :
454 : // O(1) histogram lookup by numeric id
455 : nsresult
456 8125 : internal_GetHistogramByEnumId(mozilla::Telemetry::HistogramID id, Histogram **ret,
457 : ProcessID aProcessType)
458 : {
459 : static Histogram* knownHistograms[mozilla::Telemetry::HistogramCount] = {0};
460 : static Histogram* knownContentHistograms[mozilla::Telemetry::HistogramCount] = {0};
461 : static Histogram* knownGPUHistograms[mozilla::Telemetry::HistogramCount] = {0};
462 : static Histogram* knownExtensionHistograms[mozilla::Telemetry::HistogramCount] = {0};
463 :
464 8125 : Histogram** knownList = nullptr;
465 :
466 8125 : switch (aProcessType) {
467 : case ProcessID::Parent:
468 7595 : knownList = knownHistograms;
469 7595 : break;
470 : case ProcessID::Content:
471 530 : knownList = knownContentHistograms;
472 530 : break;
473 : case ProcessID::Gpu:
474 0 : knownList = knownGPUHistograms;
475 0 : break;
476 : case ProcessID::Extension:
477 0 : knownList = knownExtensionHistograms;
478 0 : break;
479 : default:
480 0 : MOZ_ASSERT_UNREACHABLE("unknown process type");
481 : return NS_ERROR_FAILURE;
482 : }
483 :
484 8125 : Histogram* h = knownList[id];
485 8125 : if (h) {
486 3290 : *ret = h;
487 3290 : return NS_OK;
488 : }
489 :
490 4835 : const HistogramInfo &p = gHistograms[id];
491 4835 : if (p.keyed) {
492 0 : return NS_ERROR_FAILURE;
493 : }
494 :
495 9670 : nsAutoCString histogramName;
496 4835 : histogramName.Append(p.id());
497 4835 : if (const char* suffix = SuffixForProcessType(aProcessType)) {
498 26 : histogramName.AppendASCII(suffix);
499 : }
500 :
501 4835 : nsresult rv = internal_HistogramGet(histogramName.get(), p.expiration(),
502 4835 : p.histogramType, p.min, p.max,
503 9670 : p.bucketCount, true, &h);
504 4835 : if (NS_FAILED(rv))
505 0 : return rv;
506 :
507 : #ifdef DEBUG
508 : // Check that the C++ Histogram code computes the same ranges as the
509 : // Python histogram code.
510 4835 : if (!IsExpiredVersion(p.expiration())) {
511 4303 : const struct bounds &b = gBucketLowerBoundIndex[id];
512 4303 : if (b.length != 0) {
513 4303 : MOZ_ASSERT(size_t(b.length) == h->bucket_count(),
514 : "C++/Python bucket # mismatch");
515 330036 : for (int i = 0; i < b.length; ++i) {
516 325733 : MOZ_ASSERT(gBucketLowerBounds[b.offset + i] == h->ranges(i),
517 : "C++/Python bucket mismatch");
518 : }
519 : }
520 : }
521 : #endif
522 :
523 4835 : knownList[id] = h;
524 4835 : *ret = h;
525 4835 : return NS_OK;
526 : }
527 :
528 : nsresult
529 9 : internal_GetHistogramByName(const nsACString &name, Histogram **ret)
530 : {
531 : mozilla::Telemetry::HistogramID id;
532 : nsresult rv
533 9 : = internal_GetHistogramEnumId(PromiseFlatCString(name).get(), &id);
534 9 : if (NS_FAILED(rv)) {
535 0 : return rv;
536 : }
537 :
538 9 : ProcessID process = GetProcessFromName(name);
539 9 : rv = internal_GetHistogramByEnumId(id, ret, process);
540 9 : if (NS_FAILED(rv))
541 0 : return rv;
542 :
543 9 : return NS_OK;
544 : }
545 :
546 :
547 : #if !defined(MOZ_WIDGET_ANDROID)
548 :
549 : /**
550 : * This clones a histogram |existing| with the id |existingId| to a
551 : * new histogram with the name |newName|.
552 : * For simplicity this is limited to registered histograms.
553 : */
554 : Histogram*
555 15 : internal_CloneHistogram(const nsACString& newName,
556 : mozilla::Telemetry::HistogramID existingId,
557 : Histogram& existing)
558 : {
559 15 : const HistogramInfo &info = gHistograms[existingId];
560 15 : Histogram *clone = nullptr;
561 : nsresult rv;
562 :
563 75 : rv = internal_HistogramGet(PromiseFlatCString(newName).get(),
564 : info.expiration(),
565 30 : info.histogramType, existing.declared_min(),
566 30 : existing.declared_max(), existing.bucket_count(),
567 15 : true, &clone);
568 15 : if (NS_FAILED(rv)) {
569 0 : return nullptr;
570 : }
571 :
572 30 : Histogram::SampleSet ss;
573 15 : existing.SnapshotSample(&ss);
574 15 : clone->AddSampleSet(ss);
575 :
576 15 : return clone;
577 : }
578 :
579 : ProcessID
580 70 : GetProcessFromName(const std::string& aString)
581 : {
582 140 : nsDependentCString string(aString.c_str(), aString.length());
583 140 : return GetProcessFromName(string);
584 : }
585 :
586 : Histogram*
587 80 : internal_GetSubsessionHistogram(Histogram& existing)
588 : {
589 : mozilla::Telemetry::HistogramID id;
590 : nsresult rv
591 80 : = internal_GetHistogramEnumId(existing.histogram_name().c_str(), &id);
592 80 : if (NS_FAILED(rv) || gHistograms[id].keyed) {
593 10 : return nullptr;
594 : }
595 :
596 : static Histogram* subsession[mozilla::Telemetry::HistogramCount] = {};
597 : static Histogram* subsessionContent[mozilla::Telemetry::HistogramCount] = {};
598 : static Histogram* subsessionGPU[mozilla::Telemetry::HistogramCount] = {};
599 : static Histogram* subsessionExtension[mozilla::Telemetry::HistogramCount] = {};
600 :
601 70 : Histogram** cache = nullptr;
602 :
603 70 : ProcessID process = GetProcessFromName(existing.histogram_name());
604 70 : switch (process) {
605 : case ProcessID::Parent:
606 67 : cache = subsession;
607 67 : break;
608 : case ProcessID::Content:
609 3 : cache = subsessionContent;
610 3 : break;
611 : case ProcessID::Gpu:
612 0 : cache = subsessionGPU;
613 0 : break;
614 : case ProcessID::Extension:
615 0 : cache = subsessionExtension;
616 0 : break;
617 : default:
618 0 : MOZ_ASSERT_UNREACHABLE("unknown process type");
619 : return nullptr;
620 : }
621 :
622 70 : if (Histogram* cached = cache[id]) {
623 55 : return cached;
624 : }
625 :
626 15 : NS_NAMED_LITERAL_CSTRING(prefix, SUBSESSION_HISTOGRAM_PREFIX);
627 30 : nsDependentCString existingName(gHistograms[id].id());
628 15 : if (StringBeginsWith(existingName, prefix)) {
629 0 : return nullptr;
630 : }
631 :
632 30 : nsCString subsessionName(prefix);
633 15 : subsessionName.Append(existing.histogram_name().c_str());
634 :
635 15 : Histogram* clone = internal_CloneHistogram(subsessionName, id, existing);
636 15 : cache[id] = clone;
637 15 : return clone;
638 : }
639 : #endif
640 :
641 : nsresult
642 2479 : internal_HistogramAdd(Histogram& histogram, int32_t value, uint32_t dataset)
643 : {
644 : // Check if we are allowed to record the data.
645 4958 : bool canRecordDataset = CanRecordDataset(dataset,
646 2479 : internal_CanRecordBase(),
647 4958 : internal_CanRecordExtended());
648 2479 : if (!canRecordDataset || !histogram.IsRecordingEnabled()) {
649 2399 : return NS_OK;
650 : }
651 :
652 : #if !defined(MOZ_WIDGET_ANDROID)
653 80 : if (Histogram* subsession = internal_GetSubsessionHistogram(histogram)) {
654 70 : subsession->Add(value);
655 : }
656 : #endif
657 :
658 : // It is safe to add to the histogram now: the subsession histogram was already
659 : // cloned from this so we won't add the sample twice.
660 80 : histogram.Add(value);
661 :
662 80 : return NS_OK;
663 : }
664 :
665 : nsresult
666 9 : internal_HistogramAdd(Histogram& histogram, int32_t value)
667 : {
668 9 : uint32_t dataset = nsITelemetry::DATASET_RELEASE_CHANNEL_OPTIN;
669 : // We only really care about the dataset of the histogram if we are not recording
670 : // extended telemetry. Otherwise, we always record histogram data.
671 9 : if (!internal_CanRecordExtended()) {
672 : mozilla::Telemetry::HistogramID id;
673 : nsresult rv
674 9 : = internal_GetHistogramEnumId(histogram.histogram_name().c_str(), &id);
675 9 : if (NS_FAILED(rv)) {
676 : // If we can't look up the dataset, it might be because the histogram was added
677 : // at runtime. Since we're not recording extended telemetry, bail out.
678 0 : return NS_OK;
679 : }
680 9 : dataset = gHistograms[id].dataset;
681 : }
682 :
683 9 : return internal_HistogramAdd(histogram, value, dataset);
684 : }
685 :
686 : void
687 0 : internal_HistogramClear(Histogram& aHistogram, bool onlySubsession)
688 : {
689 0 : MOZ_ASSERT(XRE_IsParentProcess());
690 0 : if (!XRE_IsParentProcess()) {
691 0 : return;
692 : }
693 0 : if (!onlySubsession) {
694 0 : aHistogram.Clear();
695 : }
696 :
697 : #if !defined(MOZ_WIDGET_ANDROID)
698 0 : if (Histogram* subsession = internal_GetSubsessionHistogram(aHistogram)) {
699 0 : subsession->Clear();
700 : }
701 : #endif
702 : }
703 :
704 : } // namespace
705 :
706 :
707 : ////////////////////////////////////////////////////////////////////////
708 : ////////////////////////////////////////////////////////////////////////
709 : //
710 : // PRIVATE: Histogram corruption helpers
711 :
712 : namespace {
713 :
714 : void internal_Accumulate(mozilla::Telemetry::HistogramID aHistogram, uint32_t aSample);
715 :
716 : void
717 0 : internal_IdentifyCorruptHistograms(StatisticsRecorder::Histograms &hs)
718 : {
719 0 : for (auto h : hs) {
720 : mozilla::Telemetry::HistogramID id;
721 0 : nsresult rv = internal_GetHistogramEnumId(h->histogram_name().c_str(), &id);
722 : // This histogram isn't a static histogram, just ignore it.
723 0 : if (NS_FAILED(rv)) {
724 0 : continue;
725 : }
726 :
727 0 : if (gCorruptHistograms[id]) {
728 0 : continue;
729 : }
730 :
731 0 : Histogram::SampleSet ss;
732 0 : h->SnapshotSample(&ss);
733 :
734 0 : Histogram::Inconsistencies check = h->FindCorruption(ss);
735 0 : bool corrupt = (check != Histogram::NO_INCONSISTENCIES);
736 :
737 0 : if (corrupt) {
738 0 : mozilla::Telemetry::HistogramID corruptID = mozilla::Telemetry::HistogramCount;
739 0 : if (check & Histogram::RANGE_CHECKSUM_ERROR) {
740 0 : corruptID = mozilla::Telemetry::RANGE_CHECKSUM_ERRORS;
741 0 : } else if (check & Histogram::BUCKET_ORDER_ERROR) {
742 0 : corruptID = mozilla::Telemetry::BUCKET_ORDER_ERRORS;
743 0 : } else if (check & Histogram::COUNT_HIGH_ERROR) {
744 0 : corruptID = mozilla::Telemetry::TOTAL_COUNT_HIGH_ERRORS;
745 0 : } else if (check & Histogram::COUNT_LOW_ERROR) {
746 0 : corruptID = mozilla::Telemetry::TOTAL_COUNT_LOW_ERRORS;
747 : }
748 0 : internal_Accumulate(corruptID, 1);
749 : }
750 :
751 0 : gCorruptHistograms[id] = corrupt;
752 : }
753 0 : }
754 :
755 : } // namespace
756 :
757 :
758 : ////////////////////////////////////////////////////////////////////////
759 : ////////////////////////////////////////////////////////////////////////
760 : //
761 : // PRIVATE: Histogram reflection helpers
762 :
763 : namespace {
764 :
765 : bool
766 0 : internal_FillRanges(JSContext *cx, JS::Handle<JSObject*> array, Histogram *h)
767 : {
768 0 : JS::Rooted<JS::Value> range(cx);
769 0 : for (size_t i = 0; i < h->bucket_count(); i++) {
770 0 : range.setInt32(h->ranges(i));
771 0 : if (!JS_DefineElement(cx, array, i, range, JSPROP_ENUMERATE))
772 0 : return false;
773 : }
774 0 : return true;
775 : }
776 :
777 : enum reflectStatus
778 0 : internal_ReflectHistogramAndSamples(JSContext *cx,
779 : JS::Handle<JSObject*> obj, Histogram *h,
780 : const Histogram::SampleSet &ss)
781 : {
782 : // We don't want to reflect corrupt histograms.
783 0 : if (h->FindCorruption(ss) != Histogram::NO_INCONSISTENCIES) {
784 0 : return REFLECT_CORRUPT;
785 : }
786 :
787 0 : if (!(JS_DefineProperty(cx, obj, "min",
788 : h->declared_min(), JSPROP_ENUMERATE)
789 0 : && JS_DefineProperty(cx, obj, "max",
790 : h->declared_max(), JSPROP_ENUMERATE)
791 0 : && JS_DefineProperty(cx, obj, "histogram_type",
792 0 : h->histogram_type(), JSPROP_ENUMERATE)
793 0 : && JS_DefineProperty(cx, obj, "sum",
794 0 : double(ss.sum()), JSPROP_ENUMERATE))) {
795 0 : return REFLECT_FAILURE;
796 : }
797 :
798 0 : const size_t count = h->bucket_count();
799 0 : JS::Rooted<JSObject*> rarray(cx, JS_NewArrayObject(cx, count));
800 0 : if (!rarray) {
801 0 : return REFLECT_FAILURE;
802 : }
803 0 : if (!(internal_FillRanges(cx, rarray, h)
804 0 : && JS_DefineProperty(cx, obj, "ranges", rarray, JSPROP_ENUMERATE))) {
805 0 : return REFLECT_FAILURE;
806 : }
807 :
808 0 : JS::Rooted<JSObject*> counts_array(cx, JS_NewArrayObject(cx, count));
809 0 : if (!counts_array) {
810 0 : return REFLECT_FAILURE;
811 : }
812 0 : if (!JS_DefineProperty(cx, obj, "counts", counts_array, JSPROP_ENUMERATE)) {
813 0 : return REFLECT_FAILURE;
814 : }
815 0 : for (size_t i = 0; i < count; i++) {
816 0 : if (!JS_DefineElement(cx, counts_array, i,
817 : ss.counts(i), JSPROP_ENUMERATE)) {
818 0 : return REFLECT_FAILURE;
819 : }
820 : }
821 :
822 0 : return REFLECT_OK;
823 : }
824 :
825 : enum reflectStatus
826 0 : internal_ReflectHistogramSnapshot(JSContext *cx,
827 : JS::Handle<JSObject*> obj, Histogram *h)
828 : {
829 0 : Histogram::SampleSet ss;
830 0 : h->SnapshotSample(&ss);
831 0 : return internal_ReflectHistogramAndSamples(cx, obj, h, ss);
832 : }
833 :
834 : bool
835 0 : internal_ShouldReflectHistogram(Histogram *h)
836 : {
837 0 : const char *name = h->histogram_name().c_str();
838 : mozilla::Telemetry::HistogramID id;
839 0 : nsresult rv = internal_GetHistogramEnumId(name, &id);
840 0 : if (NS_FAILED(rv)) {
841 : // GetHistogramEnumId generally should not fail. But a lookup
842 : // failure shouldn't prevent us from reflecting histograms into JS.
843 : //
844 : // However, these two histograms are created by Histogram itself for
845 : // tracking corruption. We have our own histograms for that, so
846 : // ignore these two.
847 0 : if (strcmp(name, "Histogram.InconsistentCountHigh") == 0
848 0 : || strcmp(name, "Histogram.InconsistentCountLow") == 0) {
849 0 : return false;
850 : }
851 0 : return true;
852 : }
853 0 : return !gCorruptHistograms[id];
854 : }
855 :
856 : } // namespace
857 :
858 :
859 : ////////////////////////////////////////////////////////////////////////
860 : ////////////////////////////////////////////////////////////////////////
861 : //
862 : // PRIVATE: class KeyedHistogram
863 :
864 : namespace {
865 :
866 0 : class KeyedHistogram {
867 : public:
868 : KeyedHistogram(const nsACString &name, const nsACString &expiration,
869 : uint32_t histogramType, uint32_t min, uint32_t max,
870 : uint32_t bucketCount, uint32_t dataset);
871 : nsresult GetHistogram(const nsCString& name, Histogram** histogram, bool subsession);
872 : Histogram* GetHistogram(const nsCString& name, bool subsession);
873 6 : uint32_t GetHistogramType() const { return mHistogramType; }
874 : nsresult GetJSKeys(JSContext* cx, JS::CallArgs& args);
875 : nsresult GetJSSnapshot(JSContext* cx, JS::Handle<JSObject*> obj,
876 : bool subsession, bool clearSubsession);
877 :
878 384 : void SetRecordingEnabled(bool aEnabled) { mRecordingEnabled = aEnabled; };
879 300 : bool IsRecordingEnabled() const { return mRecordingEnabled; };
880 :
881 : nsresult Add(const nsCString& key, uint32_t aSample);
882 : void Clear(bool subsession);
883 :
884 : nsresult GetEnumId(mozilla::Telemetry::HistogramID& id);
885 :
886 : private:
887 : typedef nsBaseHashtableET<nsCStringHashKey, Histogram*> KeyedHistogramEntry;
888 : typedef AutoHashtable<KeyedHistogramEntry> KeyedHistogramMapType;
889 : KeyedHistogramMapType mHistogramMap;
890 : #if !defined(MOZ_WIDGET_ANDROID)
891 : KeyedHistogramMapType mSubsessionMap;
892 : #endif
893 :
894 : static bool ReflectKeyedHistogram(KeyedHistogramEntry* entry,
895 : JSContext* cx,
896 : JS::Handle<JSObject*> obj);
897 :
898 : const nsCString mName;
899 : const nsCString mExpiration;
900 : const uint32_t mHistogramType;
901 : const uint32_t mMin;
902 : const uint32_t mMax;
903 : const uint32_t mBucketCount;
904 : const uint32_t mDataset;
905 : mozilla::Atomic<bool, mozilla::Relaxed> mRecordingEnabled;
906 : };
907 :
908 762 : KeyedHistogram::KeyedHistogram(const nsACString &name,
909 : const nsACString &expiration,
910 : uint32_t histogramType,
911 : uint32_t min, uint32_t max,
912 762 : uint32_t bucketCount, uint32_t dataset)
913 : : mHistogramMap()
914 : #if !defined(MOZ_WIDGET_ANDROID)
915 : , mSubsessionMap()
916 : #endif
917 : , mName(name)
918 : , mExpiration(expiration)
919 : , mHistogramType(histogramType)
920 : , mMin(min)
921 : , mMax(max)
922 : , mBucketCount(bucketCount)
923 : , mDataset(dataset)
924 762 : , mRecordingEnabled(true)
925 : {
926 762 : }
927 :
928 : nsresult
929 12 : KeyedHistogram::GetHistogram(const nsCString& key, Histogram** histogram,
930 : bool subsession)
931 : {
932 : #if !defined(MOZ_WIDGET_ANDROID)
933 12 : KeyedHistogramMapType& map = subsession ? mSubsessionMap : mHistogramMap;
934 : #else
935 : KeyedHistogramMapType& map = mHistogramMap;
936 : #endif
937 12 : KeyedHistogramEntry* entry = map.GetEntry(key);
938 12 : if (entry) {
939 8 : *histogram = entry->mData;
940 8 : return NS_OK;
941 : }
942 :
943 8 : nsCString histogramName;
944 : #if !defined(MOZ_WIDGET_ANDROID)
945 4 : if (subsession) {
946 2 : histogramName.AppendLiteral(SUBSESSION_HISTOGRAM_PREFIX);
947 : }
948 : #endif
949 4 : histogramName.Append(mName);
950 4 : histogramName.AppendLiteral(KEYED_HISTOGRAM_NAME_SEPARATOR);
951 4 : histogramName.Append(key);
952 :
953 : Histogram* h;
954 4 : nsresult rv = internal_HistogramGet(histogramName.get(), mExpiration.get(),
955 4 : mHistogramType, mMin, mMax, mBucketCount,
956 4 : true, &h);
957 4 : if (NS_FAILED(rv)) {
958 0 : return rv;
959 : }
960 :
961 4 : h->ClearFlags(Histogram::kUmaTargetedHistogramFlag);
962 4 : *histogram = h;
963 :
964 4 : entry = map.PutEntry(key);
965 4 : if (MOZ_UNLIKELY(!entry)) {
966 0 : return NS_ERROR_OUT_OF_MEMORY;
967 : }
968 :
969 4 : entry->mData = h;
970 4 : return NS_OK;
971 : }
972 :
973 : Histogram*
974 12 : KeyedHistogram::GetHistogram(const nsCString& key, bool subsession)
975 : {
976 12 : Histogram* h = nullptr;
977 12 : if (NS_FAILED(GetHistogram(key, &h, subsession))) {
978 0 : return nullptr;
979 : }
980 12 : return h;
981 : }
982 :
983 : nsresult
984 1173 : KeyedHistogram::Add(const nsCString& key, uint32_t sample)
985 : {
986 2346 : bool canRecordDataset = CanRecordDataset(mDataset,
987 1173 : internal_CanRecordBase(),
988 2346 : internal_CanRecordExtended());
989 1173 : if (!canRecordDataset || !IsRecordingEnabled()) {
990 1167 : return NS_OK;
991 : }
992 :
993 6 : Histogram* histogram = GetHistogram(key, false);
994 6 : MOZ_ASSERT(histogram);
995 6 : if (!histogram) {
996 0 : return NS_ERROR_FAILURE;
997 : }
998 : #if !defined(MOZ_WIDGET_ANDROID)
999 6 : Histogram* subsession = GetHistogram(key, true);
1000 6 : MOZ_ASSERT(subsession);
1001 6 : if (!subsession) {
1002 0 : return NS_ERROR_FAILURE;
1003 : }
1004 : #endif
1005 :
1006 6 : histogram->Add(sample);
1007 : #if !defined(MOZ_WIDGET_ANDROID)
1008 6 : subsession->Add(sample);
1009 : #endif
1010 6 : return NS_OK;
1011 : }
1012 :
1013 : void
1014 0 : KeyedHistogram::Clear(bool onlySubsession)
1015 : {
1016 0 : MOZ_ASSERT(XRE_IsParentProcess());
1017 0 : if (!XRE_IsParentProcess()) {
1018 0 : return;
1019 : }
1020 : #if !defined(MOZ_WIDGET_ANDROID)
1021 0 : for (auto iter = mSubsessionMap.Iter(); !iter.Done(); iter.Next()) {
1022 0 : iter.Get()->mData->Clear();
1023 : }
1024 0 : mSubsessionMap.Clear();
1025 0 : if (onlySubsession) {
1026 0 : return;
1027 : }
1028 : #endif
1029 :
1030 0 : for (auto iter = mHistogramMap.Iter(); !iter.Done(); iter.Next()) {
1031 0 : iter.Get()->mData->Clear();
1032 : }
1033 0 : mHistogramMap.Clear();
1034 : }
1035 :
1036 : nsresult
1037 0 : KeyedHistogram::GetJSKeys(JSContext* cx, JS::CallArgs& args)
1038 : {
1039 0 : JS::AutoValueVector keys(cx);
1040 0 : if (!keys.reserve(mHistogramMap.Count())) {
1041 0 : return NS_ERROR_OUT_OF_MEMORY;
1042 : }
1043 :
1044 0 : for (auto iter = mHistogramMap.Iter(); !iter.Done(); iter.Next()) {
1045 0 : JS::RootedValue jsKey(cx);
1046 0 : const NS_ConvertUTF8toUTF16 key(iter.Get()->GetKey());
1047 0 : jsKey.setString(JS_NewUCStringCopyN(cx, key.Data(), key.Length()));
1048 0 : if (!keys.append(jsKey)) {
1049 0 : return NS_ERROR_OUT_OF_MEMORY;
1050 : }
1051 : }
1052 :
1053 0 : JS::RootedObject jsKeys(cx, JS_NewArrayObject(cx, keys));
1054 0 : if (!jsKeys) {
1055 0 : return NS_ERROR_FAILURE;
1056 : }
1057 :
1058 0 : args.rval().setObject(*jsKeys);
1059 0 : return NS_OK;
1060 : }
1061 :
1062 : bool
1063 0 : KeyedHistogram::ReflectKeyedHistogram(KeyedHistogramEntry* entry,
1064 : JSContext* cx, JS::Handle<JSObject*> obj)
1065 : {
1066 0 : JS::RootedObject histogramSnapshot(cx, JS_NewPlainObject(cx));
1067 0 : if (!histogramSnapshot) {
1068 0 : return false;
1069 : }
1070 :
1071 0 : if (internal_ReflectHistogramSnapshot(cx, histogramSnapshot,
1072 : entry->mData) != REFLECT_OK) {
1073 0 : return false;
1074 : }
1075 :
1076 0 : const NS_ConvertUTF8toUTF16 key(entry->GetKey());
1077 0 : if (!JS_DefineUCProperty(cx, obj, key.Data(), key.Length(),
1078 : histogramSnapshot, JSPROP_ENUMERATE)) {
1079 0 : return false;
1080 : }
1081 :
1082 0 : return true;
1083 : }
1084 :
1085 : nsresult
1086 0 : KeyedHistogram::GetJSSnapshot(JSContext* cx, JS::Handle<JSObject*> obj,
1087 : bool subsession, bool clearSubsession)
1088 : {
1089 : #if !defined(MOZ_WIDGET_ANDROID)
1090 0 : KeyedHistogramMapType& map = subsession ? mSubsessionMap : mHistogramMap;
1091 : #else
1092 : KeyedHistogramMapType& map = mHistogramMap;
1093 : #endif
1094 0 : if (!map.ReflectIntoJS(&KeyedHistogram::ReflectKeyedHistogram, cx, obj)) {
1095 0 : return NS_ERROR_FAILURE;
1096 : }
1097 :
1098 : #if !defined(MOZ_WIDGET_ANDROID)
1099 0 : if (subsession && clearSubsession) {
1100 0 : Clear(true);
1101 : }
1102 : #endif
1103 :
1104 0 : return NS_OK;
1105 : }
1106 :
1107 : nsresult
1108 6 : KeyedHistogram::GetEnumId(mozilla::Telemetry::HistogramID& id)
1109 : {
1110 6 : return internal_GetHistogramEnumId(mName.get(), &id);
1111 : }
1112 :
1113 : } // namespace
1114 :
1115 :
1116 : ////////////////////////////////////////////////////////////////////////
1117 : ////////////////////////////////////////////////////////////////////////
1118 : //
1119 : // PRIVATE: KeyedHistogram helpers
1120 :
1121 : namespace {
1122 :
1123 : KeyedHistogram*
1124 1851 : internal_GetKeyedHistogramById(const nsACString &name)
1125 : {
1126 1851 : if (!gInitDone) {
1127 0 : return nullptr;
1128 : }
1129 :
1130 1851 : KeyedHistogram* keyed = nullptr;
1131 1851 : gKeyedHistograms.Get(name, &keyed);
1132 1851 : return keyed;
1133 : }
1134 :
1135 : } // namespace
1136 :
1137 :
1138 : ////////////////////////////////////////////////////////////////////////
1139 : ////////////////////////////////////////////////////////////////////////
1140 : //
1141 : // PRIVATE: thread-unsafe helpers for the external interface
1142 :
1143 : // This is a StaticMutex rather than a plain Mutex (1) so that
1144 : // it gets initialised in a thread-safe manner the first time
1145 : // it is used, and (2) because it is never de-initialised, and
1146 : // a normal Mutex would show up as a leak in BloatView. StaticMutex
1147 : // also has the "OffTheBooks" property, so it won't show as a leak
1148 : // in BloatView.
1149 3 : static StaticMutex gTelemetryHistogramMutex;
1150 :
1151 : namespace {
1152 :
1153 : void
1154 5199 : internal_SetHistogramRecordingEnabled(mozilla::Telemetry::HistogramID aID, bool aEnabled)
1155 : {
1156 5199 : if (gHistograms[aID].keyed) {
1157 384 : const nsDependentCString id(gHistograms[aID].id());
1158 384 : KeyedHistogram* keyed = internal_GetKeyedHistogramById(id);
1159 384 : if (keyed) {
1160 384 : keyed->SetRecordingEnabled(aEnabled);
1161 384 : return;
1162 : }
1163 : } else {
1164 : Histogram *h;
1165 4815 : nsresult rv = internal_GetHistogramByEnumId(aID, &h, ProcessID::Parent);
1166 4815 : if (NS_SUCCEEDED(rv)) {
1167 4815 : h->SetRecordingEnabled(aEnabled);
1168 4815 : return;
1169 : }
1170 : }
1171 :
1172 0 : MOZ_ASSERT(false, "Telemetry::SetHistogramRecordingEnabled(...) id not found");
1173 : }
1174 :
1175 : bool
1176 2771 : internal_RemoteAccumulate(mozilla::Telemetry::HistogramID aId, uint32_t aSample)
1177 : {
1178 2771 : if (XRE_IsParentProcess()) {
1179 1940 : return false;
1180 : }
1181 : Histogram *h;
1182 831 : nsresult rv = internal_GetHistogramByEnumId(aId, &h, ProcessID::Parent);
1183 831 : if (NS_SUCCEEDED(rv) && !h->IsRecordingEnabled()) {
1184 3 : return true;
1185 : }
1186 828 : TelemetryIPCAccumulator::AccumulateChildHistogram(aId, aSample);
1187 828 : return true;
1188 : }
1189 :
1190 : bool
1191 1290 : internal_RemoteAccumulate(mozilla::Telemetry::HistogramID aId,
1192 : const nsCString& aKey, uint32_t aSample)
1193 : {
1194 1290 : if (XRE_IsParentProcess()) {
1195 996 : return false;
1196 : }
1197 294 : const HistogramInfo& th = gHistograms[aId];
1198 : KeyedHistogram* keyed
1199 294 : = internal_GetKeyedHistogramById(nsDependentCString(th.id()));
1200 294 : MOZ_ASSERT(keyed);
1201 294 : if (!keyed->IsRecordingEnabled()) {
1202 0 : return false;
1203 : }
1204 294 : TelemetryIPCAccumulator::AccumulateChildKeyedHistogram(aId, aKey, aSample);
1205 294 : return true;
1206 : }
1207 :
1208 2784 : void internal_Accumulate(mozilla::Telemetry::HistogramID aHistogram, uint32_t aSample)
1209 : {
1210 5555 : if (!internal_CanRecordBase() ||
1211 2771 : internal_RemoteAccumulate(aHistogram, aSample)) {
1212 844 : return;
1213 : }
1214 : Histogram *h;
1215 1940 : nsresult rv = internal_GetHistogramByEnumId(aHistogram, &h, ProcessID::Parent);
1216 1940 : if (NS_SUCCEEDED(rv)) {
1217 1940 : internal_HistogramAdd(*h, aSample, gHistograms[aHistogram].dataset);
1218 : }
1219 : }
1220 :
1221 : void
1222 1284 : internal_Accumulate(mozilla::Telemetry::HistogramID aID,
1223 : const nsCString& aKey, uint32_t aSample)
1224 : {
1225 2568 : if (!gInitDone || !internal_CanRecordBase() ||
1226 1284 : internal_RemoteAccumulate(aID, aKey, aSample)) {
1227 288 : return;
1228 : }
1229 996 : const HistogramInfo& th = gHistograms[aID];
1230 : KeyedHistogram* keyed
1231 996 : = internal_GetKeyedHistogramById(nsDependentCString(th.id()));
1232 996 : MOZ_ASSERT(keyed);
1233 996 : keyed->Add(aKey, aSample);
1234 : }
1235 :
1236 : void
1237 9 : internal_Accumulate(Histogram& aHistogram, uint32_t aSample)
1238 : {
1239 9 : if (XRE_IsParentProcess()) {
1240 9 : internal_HistogramAdd(aHistogram, aSample);
1241 9 : return;
1242 : }
1243 :
1244 : mozilla::Telemetry::HistogramID id;
1245 0 : nsresult rv = internal_GetHistogramEnumId(aHistogram.histogram_name().c_str(), &id);
1246 0 : if (NS_SUCCEEDED(rv)) {
1247 0 : internal_RemoteAccumulate(id, aSample);
1248 : }
1249 : }
1250 :
1251 : void
1252 6 : internal_Accumulate(KeyedHistogram& aKeyed,
1253 : const nsCString& aKey, uint32_t aSample)
1254 : {
1255 6 : if (XRE_IsParentProcess()) {
1256 0 : aKeyed.Add(aKey, aSample);
1257 0 : return;
1258 : }
1259 :
1260 : mozilla::Telemetry::HistogramID id;
1261 6 : if (NS_SUCCEEDED(aKeyed.GetEnumId(id))) {
1262 6 : internal_RemoteAccumulate(id, aKey, aSample);
1263 : }
1264 : }
1265 :
1266 : void
1267 530 : internal_AccumulateChild(ProcessID aProcessType, mozilla::Telemetry::HistogramID aId, uint32_t aSample)
1268 : {
1269 530 : if (!internal_CanRecordBase()) {
1270 0 : return;
1271 : }
1272 : Histogram* h;
1273 530 : nsresult rv = internal_GetHistogramByEnumId(aId, &h, aProcessType);
1274 530 : if (NS_SUCCEEDED(rv)) {
1275 530 : internal_HistogramAdd(*h, aSample, gHistograms[aId].dataset);
1276 : } else {
1277 0 : NS_WARNING("NS_FAILED GetHistogramByEnumId for CHILD");
1278 : }
1279 : }
1280 :
1281 : void
1282 177 : internal_AccumulateChildKeyed(ProcessID aProcessType, mozilla::Telemetry::HistogramID aId,
1283 : const nsCString& aKey, uint32_t aSample)
1284 : {
1285 177 : if (!gInitDone || !internal_CanRecordBase()) {
1286 0 : return;
1287 : }
1288 :
1289 177 : const char* suffix = SuffixForProcessType(aProcessType);
1290 177 : if (!suffix) {
1291 0 : MOZ_ASSERT_UNREACHABLE("suffix should not be null");
1292 : return;
1293 : }
1294 :
1295 177 : const HistogramInfo& th = gHistograms[aId];
1296 :
1297 354 : nsAutoCString id;
1298 177 : id.Append(th.id());
1299 177 : id.AppendASCII(suffix);
1300 :
1301 177 : KeyedHistogram* keyed = internal_GetKeyedHistogramById(id);
1302 177 : MOZ_ASSERT(keyed);
1303 177 : keyed->Add(aKey, aSample);
1304 : }
1305 :
1306 : } // namespace
1307 :
1308 :
1309 : ////////////////////////////////////////////////////////////////////////
1310 : ////////////////////////////////////////////////////////////////////////
1311 : //
1312 : // PRIVATE: JSHistogram_* functions
1313 :
1314 : // NOTE: the functions in this section:
1315 : //
1316 : // internal_JSHistogram_Add
1317 : // internal_JSHistogram_Snapshot
1318 : // internal_JSHistogram_Clear
1319 : // internal_WrapAndReturnHistogram
1320 : //
1321 : // all run without protection from |gTelemetryHistogramMutex|. If they
1322 : // held |gTelemetryHistogramMutex|, there would be the possibility of
1323 : // deadlock because the JS_ calls that they make may call back into the
1324 : // TelemetryHistogram interface, hence trying to re-acquire the mutex.
1325 : //
1326 : // This means that these functions potentially race against threads, but
1327 : // that seems preferable to risking deadlock.
1328 :
1329 : namespace {
1330 :
1331 : static const JSClass sJSHistogramClass = {
1332 : "JSHistogram", /* name */
1333 : JSCLASS_HAS_PRIVATE /* flags */
1334 : };
1335 :
1336 : bool
1337 9 : internal_JSHistogram_Add(JSContext *cx, unsigned argc, JS::Value *vp)
1338 : {
1339 9 : JSObject *obj = JS_THIS_OBJECT(cx, vp);
1340 9 : MOZ_ASSERT(obj);
1341 18 : if (!obj ||
1342 9 : JS_GetClass(obj) != &sJSHistogramClass) {
1343 0 : return false;
1344 : }
1345 :
1346 9 : Histogram *h = static_cast<Histogram*>(JS_GetPrivate(obj));
1347 9 : MOZ_ASSERT(h);
1348 9 : Histogram::ClassType type = h->histogram_type();
1349 :
1350 9 : JS::CallArgs args = CallArgsFromVp(argc, vp);
1351 : // This function should always return |undefined| and never fail but
1352 : // rather report failures using the console.
1353 9 : args.rval().setUndefined();
1354 :
1355 9 : if (!internal_CanRecordBase()) {
1356 0 : return true;
1357 : }
1358 :
1359 9 : uint32_t value = 0;
1360 : mozilla::Telemetry::HistogramID id;
1361 9 : if ((type == base::CountHistogram::COUNT_HISTOGRAM) && (args.length() == 0)) {
1362 : // If we don't have an argument for the count histogram, assume an increment of 1.
1363 : // Otherwise, make sure to run some sanity checks on the argument.
1364 0 : value = 1;
1365 27 : } else if (type == base::LinearHistogram::LINEAR_HISTOGRAM &&
1366 15 : (args.length() > 0) && args[0].isString() &&
1367 9 : NS_SUCCEEDED(internal_GetHistogramEnumId(h->histogram_name().c_str(), &id)) &&
1368 0 : gHistograms[id].histogramType == nsITelemetry::HISTOGRAM_CATEGORICAL) {
1369 : // For categorical histograms we allow passing a string argument that specifies the label.
1370 0 : nsAutoJSString label;
1371 0 : if (!label.init(cx, args[0])) {
1372 0 : LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Invalid string parameter"));
1373 0 : return true;
1374 : }
1375 :
1376 : // Get label id value.
1377 0 : nsresult rv = gHistograms[id].label_id(NS_ConvertUTF16toUTF8(label).get(), &value);
1378 0 : if (NS_FAILED(rv)) {
1379 0 : LogToBrowserConsole(nsIScriptError::errorFlag,
1380 0 : NS_LITERAL_STRING("Unknown label for categorical histogram"));
1381 0 : return true;
1382 : }
1383 : } else {
1384 : // All other accumulations expect one numerical argument.
1385 9 : if (!args.length()) {
1386 0 : LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Expected one argument"));
1387 0 : return true;
1388 : }
1389 :
1390 9 : if (!(args[0].isNumber() || args[0].isBoolean())) {
1391 0 : LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Not a number"));
1392 0 : return true;
1393 : }
1394 :
1395 9 : if (!JS::ToUint32(cx, args[0], &value)) {
1396 0 : LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Failed to convert argument"));
1397 0 : return true;
1398 : }
1399 : }
1400 :
1401 : {
1402 18 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
1403 9 : internal_Accumulate(*h, value);
1404 : }
1405 9 : return true;
1406 : }
1407 :
1408 : bool
1409 0 : internal_JSHistogram_Snapshot(JSContext *cx, unsigned argc, JS::Value *vp)
1410 : {
1411 0 : JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
1412 0 : JSObject *obj = JS_THIS_OBJECT(cx, vp);
1413 0 : if (!obj ||
1414 0 : JS_GetClass(obj) != &sJSHistogramClass) {
1415 0 : return false;
1416 : }
1417 :
1418 0 : Histogram *h = static_cast<Histogram*>(JS_GetPrivate(obj));
1419 0 : JS::Rooted<JSObject*> snapshot(cx, JS_NewPlainObject(cx));
1420 0 : if (!snapshot)
1421 0 : return false;
1422 :
1423 0 : switch (internal_ReflectHistogramSnapshot(cx, snapshot, h)) {
1424 : case REFLECT_FAILURE:
1425 0 : return false;
1426 : case REFLECT_CORRUPT:
1427 0 : JS_ReportErrorASCII(cx, "Histogram is corrupt");
1428 0 : return false;
1429 : case REFLECT_OK:
1430 0 : args.rval().setObject(*snapshot);
1431 0 : return true;
1432 : default:
1433 0 : MOZ_CRASH("unhandled reflection status");
1434 : }
1435 : }
1436 :
1437 : bool
1438 0 : internal_JSHistogram_Clear(JSContext *cx, unsigned argc, JS::Value *vp)
1439 : {
1440 0 : JSObject *obj = JS_THIS_OBJECT(cx, vp);
1441 0 : if (!obj ||
1442 0 : JS_GetClass(obj) != &sJSHistogramClass) {
1443 0 : return false;
1444 : }
1445 :
1446 0 : bool onlySubsession = false;
1447 0 : JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
1448 : // This function should always return |undefined| and never fail but
1449 : // rather report failures using the console.
1450 0 : args.rval().setUndefined();
1451 :
1452 :
1453 : #if !defined(MOZ_WIDGET_ANDROID)
1454 0 : if (args.length() >= 1) {
1455 0 : if (!args[0].isBoolean()) {
1456 0 : JS_ReportErrorASCII(cx, "Not a boolean");
1457 0 : return false;
1458 : }
1459 :
1460 0 : onlySubsession = JS::ToBoolean(args[0]);
1461 : }
1462 : #endif
1463 :
1464 0 : Histogram *h = static_cast<Histogram*>(JS_GetPrivate(obj));
1465 0 : MOZ_ASSERT(h);
1466 0 : if (h) {
1467 0 : internal_HistogramClear(*h, onlySubsession);
1468 : }
1469 :
1470 0 : return true;
1471 : }
1472 :
1473 : // NOTE: Runs without protection from |gTelemetryHistogramMutex|.
1474 : // See comment at the top of this section.
1475 : nsresult
1476 9 : internal_WrapAndReturnHistogram(Histogram *h, JSContext *cx,
1477 : JS::MutableHandle<JS::Value> ret)
1478 : {
1479 18 : JS::Rooted<JSObject*> obj(cx, JS_NewObject(cx, &sJSHistogramClass));
1480 9 : if (!obj)
1481 0 : return NS_ERROR_FAILURE;
1482 : // The 3 functions that are wrapped up here are eventually called
1483 : // by the same thread that runs this function.
1484 36 : if (!(JS_DefineFunction(cx, obj, "add", internal_JSHistogram_Add, 1, 0)
1485 18 : && JS_DefineFunction(cx, obj, "snapshot",
1486 : internal_JSHistogram_Snapshot, 0, 0)
1487 27 : && JS_DefineFunction(cx, obj, "clear", internal_JSHistogram_Clear, 0, 0))) {
1488 0 : return NS_ERROR_FAILURE;
1489 : }
1490 9 : JS_SetPrivate(obj, h);
1491 9 : ret.setObject(*obj);
1492 9 : return NS_OK;
1493 : }
1494 :
1495 : } // namespace
1496 :
1497 :
1498 : ////////////////////////////////////////////////////////////////////////
1499 : ////////////////////////////////////////////////////////////////////////
1500 : //
1501 : // PRIVATE: JSKeyedHistogram_* functions
1502 :
1503 : // NOTE: the functions in this section:
1504 : //
1505 : // internal_KeyedHistogram_SnapshotImpl
1506 : // internal_JSKeyedHistogram_Add
1507 : // internal_JSKeyedHistogram_Keys
1508 : // internal_JSKeyedHistogram_Snapshot
1509 : // internal_JSKeyedHistogram_SubsessionSnapshot
1510 : // internal_JSKeyedHistogram_SnapshotSubsessionAndClear
1511 : // internal_JSKeyedHistogram_Clear
1512 : // internal_WrapAndReturnKeyedHistogram
1513 : //
1514 : // Same comments as above, at the JSHistogram_* section, regarding
1515 : // deadlock avoidance, apply.
1516 :
1517 : namespace {
1518 :
1519 : static const JSClass sJSKeyedHistogramClass = {
1520 : "JSKeyedHistogram", /* name */
1521 : JSCLASS_HAS_PRIVATE /* flags */
1522 : };
1523 :
1524 : bool
1525 0 : internal_KeyedHistogram_SnapshotImpl(JSContext *cx, unsigned argc,
1526 : JS::Value *vp,
1527 : bool subsession, bool clearSubsession)
1528 : {
1529 0 : JSObject *obj = JS_THIS_OBJECT(cx, vp);
1530 0 : if (!obj ||
1531 0 : JS_GetClass(obj) != &sJSKeyedHistogramClass) {
1532 0 : return false;
1533 : }
1534 :
1535 0 : KeyedHistogram* keyed = static_cast<KeyedHistogram*>(JS_GetPrivate(obj));
1536 0 : if (!keyed) {
1537 0 : return false;
1538 : }
1539 :
1540 0 : JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
1541 :
1542 0 : if (args.length() == 0) {
1543 0 : JS::RootedObject snapshot(cx, JS_NewPlainObject(cx));
1544 0 : if (!snapshot) {
1545 0 : JS_ReportErrorASCII(cx, "Failed to create object");
1546 0 : return false;
1547 : }
1548 :
1549 0 : if (!NS_SUCCEEDED(keyed->GetJSSnapshot(cx, snapshot, subsession, clearSubsession))) {
1550 0 : JS_ReportErrorASCII(cx, "Failed to reflect keyed histograms");
1551 0 : return false;
1552 : }
1553 :
1554 0 : args.rval().setObject(*snapshot);
1555 0 : return true;
1556 : }
1557 :
1558 0 : nsAutoJSString key;
1559 0 : if (!args[0].isString() || !key.init(cx, args[0])) {
1560 0 : JS_ReportErrorASCII(cx, "Not a string");
1561 0 : return false;
1562 : }
1563 :
1564 0 : Histogram* h = nullptr;
1565 0 : nsresult rv = keyed->GetHistogram(NS_ConvertUTF16toUTF8(key), &h, subsession);
1566 0 : if (NS_FAILED(rv)) {
1567 0 : JS_ReportErrorASCII(cx, "Failed to get histogram");
1568 0 : return false;
1569 : }
1570 :
1571 0 : JS::RootedObject snapshot(cx, JS_NewPlainObject(cx));
1572 0 : if (!snapshot) {
1573 0 : return false;
1574 : }
1575 :
1576 0 : switch (internal_ReflectHistogramSnapshot(cx, snapshot, h)) {
1577 : case REFLECT_FAILURE:
1578 0 : return false;
1579 : case REFLECT_CORRUPT:
1580 0 : JS_ReportErrorASCII(cx, "Histogram is corrupt");
1581 0 : return false;
1582 : case REFLECT_OK:
1583 0 : args.rval().setObject(*snapshot);
1584 0 : return true;
1585 : default:
1586 0 : MOZ_CRASH("unhandled reflection status");
1587 : }
1588 : }
1589 :
1590 : bool
1591 6 : internal_JSKeyedHistogram_Add(JSContext *cx, unsigned argc, JS::Value *vp)
1592 : {
1593 6 : JSObject *obj = JS_THIS_OBJECT(cx, vp);
1594 12 : if (!obj ||
1595 6 : JS_GetClass(obj) != &sJSKeyedHistogramClass) {
1596 0 : return false;
1597 : }
1598 :
1599 6 : KeyedHistogram* keyed = static_cast<KeyedHistogram*>(JS_GetPrivate(obj));
1600 6 : if (!keyed) {
1601 0 : return false;
1602 : }
1603 :
1604 6 : JS::CallArgs args = CallArgsFromVp(argc, vp);
1605 : // This function should always return |undefined| and never fail but
1606 : // rather report failures using the console.
1607 6 : args.rval().setUndefined();
1608 6 : if (args.length() < 1) {
1609 0 : LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Expected one argument"));
1610 0 : return true;
1611 : }
1612 :
1613 12 : nsAutoJSString key;
1614 6 : if (!args[0].isString() || !key.init(cx, args[0])) {
1615 0 : LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Not a string"));
1616 0 : return true;
1617 : }
1618 :
1619 6 : const uint32_t type = keyed->GetHistogramType();
1620 :
1621 : // If we don't have an argument for the count histogram, assume an increment of 1.
1622 : // Otherwise, make sure to run some sanity checks on the argument.
1623 6 : uint32_t value = 1;
1624 6 : if ((type != nsITelemetry::HISTOGRAM_COUNT) || (args.length() == 2)) {
1625 6 : if (args.length() < 2) {
1626 0 : LogToBrowserConsole(nsIScriptError::errorFlag,
1627 0 : NS_LITERAL_STRING("Expected two arguments for this histogram type"));
1628 0 : return true;
1629 : }
1630 :
1631 6 : if (type == nsITelemetry::HISTOGRAM_CATEGORICAL && args[1].isString()) {
1632 : // For categorical histograms we allow passing a string argument that specifies the label.
1633 : mozilla::Telemetry::HistogramID id;
1634 0 : if (NS_FAILED(keyed->GetEnumId(id))) {
1635 0 : LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Failed to get histogram id."));
1636 0 : return true;
1637 : }
1638 :
1639 : // Get label string.
1640 0 : nsAutoJSString label;
1641 0 : if (!label.init(cx, args[1])) {
1642 0 : LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Invalid string parameter"));
1643 0 : return true;
1644 : }
1645 :
1646 : // Get label id value.
1647 0 : nsresult rv = gHistograms[id].label_id(NS_ConvertUTF16toUTF8(label).get(), &value);
1648 0 : if (NS_FAILED(rv)) {
1649 0 : LogToBrowserConsole(nsIScriptError::errorFlag,
1650 0 : NS_LITERAL_STRING("Unknown label for categorical histogram"));
1651 0 : return true;
1652 : }
1653 : } else {
1654 : // All other accumulations expect one numerical argument.
1655 6 : if (!(args[1].isNumber() || args[1].isBoolean())) {
1656 0 : LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Not a number"));
1657 0 : return true;
1658 : }
1659 :
1660 6 : if (!JS::ToUint32(cx, args[1], &value)) {
1661 0 : LogToBrowserConsole(nsIScriptError::errorFlag, NS_LITERAL_STRING("Failed to convert argument"));
1662 0 : return true;
1663 : }
1664 : }
1665 : }
1666 :
1667 : {
1668 12 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
1669 6 : internal_Accumulate(*keyed, NS_ConvertUTF16toUTF8(key), value);
1670 : }
1671 6 : return true;
1672 : }
1673 :
1674 : bool
1675 0 : internal_JSKeyedHistogram_Keys(JSContext *cx, unsigned argc, JS::Value *vp)
1676 : {
1677 0 : JSObject *obj = JS_THIS_OBJECT(cx, vp);
1678 0 : if (!obj ||
1679 0 : JS_GetClass(obj) != &sJSKeyedHistogramClass) {
1680 0 : return false;
1681 : }
1682 :
1683 0 : KeyedHistogram* keyed = static_cast<KeyedHistogram*>(JS_GetPrivate(obj));
1684 0 : if (!keyed) {
1685 0 : return false;
1686 : }
1687 :
1688 0 : JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
1689 0 : return NS_SUCCEEDED(keyed->GetJSKeys(cx, args));
1690 : }
1691 :
1692 : bool
1693 0 : internal_JSKeyedHistogram_Snapshot(JSContext *cx, unsigned argc, JS::Value *vp)
1694 : {
1695 0 : return internal_KeyedHistogram_SnapshotImpl(cx, argc, vp, false, false);
1696 : }
1697 :
1698 : #if !defined(MOZ_WIDGET_ANDROID)
1699 : bool
1700 0 : internal_JSKeyedHistogram_SubsessionSnapshot(JSContext *cx,
1701 : unsigned argc, JS::Value *vp)
1702 : {
1703 0 : return internal_KeyedHistogram_SnapshotImpl(cx, argc, vp, true, false);
1704 : }
1705 : #endif
1706 :
1707 : #if !defined(MOZ_WIDGET_ANDROID)
1708 : bool
1709 0 : internal_JSKeyedHistogram_SnapshotSubsessionAndClear(JSContext *cx,
1710 : unsigned argc,
1711 : JS::Value *vp)
1712 : {
1713 0 : JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
1714 0 : if (args.length() != 0) {
1715 0 : JS_ReportErrorASCII(cx, "No key arguments supported for snapshotSubsessionAndClear");
1716 : }
1717 :
1718 0 : return internal_KeyedHistogram_SnapshotImpl(cx, argc, vp, true, true);
1719 : }
1720 : #endif
1721 :
1722 : bool
1723 0 : internal_JSKeyedHistogram_Clear(JSContext *cx, unsigned argc, JS::Value *vp)
1724 : {
1725 0 : JSObject *obj = JS_THIS_OBJECT(cx, vp);
1726 0 : if (!obj ||
1727 0 : JS_GetClass(obj) != &sJSKeyedHistogramClass) {
1728 0 : return false;
1729 : }
1730 :
1731 0 : KeyedHistogram* keyed = static_cast<KeyedHistogram*>(JS_GetPrivate(obj));
1732 0 : if (!keyed) {
1733 0 : return false;
1734 : }
1735 :
1736 0 : JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
1737 : // This function should always return |undefined| and never fail but
1738 : // rather report failures using the console.
1739 0 : args.rval().setUndefined();
1740 :
1741 : #if !defined(MOZ_WIDGET_ANDROID)
1742 0 : bool onlySubsession = false;
1743 :
1744 0 : if (args.length() >= 1) {
1745 0 : if (!(args[0].isNumber() || args[0].isBoolean())) {
1746 0 : JS_ReportErrorASCII(cx, "Not a boolean");
1747 0 : return false;
1748 : }
1749 :
1750 0 : onlySubsession = JS::ToBoolean(args[0]);
1751 : }
1752 :
1753 0 : keyed->Clear(onlySubsession);
1754 : #else
1755 : keyed->Clear(false);
1756 : #endif
1757 0 : return true;
1758 : }
1759 :
1760 : // NOTE: Runs without protection from |gTelemetryHistogramMutex|.
1761 : // See comment at the top of this section.
1762 : nsresult
1763 6 : internal_WrapAndReturnKeyedHistogram(KeyedHistogram *h, JSContext *cx,
1764 : JS::MutableHandle<JS::Value> ret)
1765 : {
1766 12 : JS::Rooted<JSObject*> obj(cx, JS_NewObject(cx, &sJSKeyedHistogramClass));
1767 6 : if (!obj)
1768 0 : return NS_ERROR_FAILURE;
1769 : // The 6 functions that are wrapped up here are eventually called
1770 : // by the same thread that runs this function.
1771 24 : if (!(JS_DefineFunction(cx, obj, "add", internal_JSKeyedHistogram_Add, 2, 0)
1772 12 : && JS_DefineFunction(cx, obj, "snapshot",
1773 : internal_JSKeyedHistogram_Snapshot, 1, 0)
1774 : #if !defined(MOZ_WIDGET_ANDROID)
1775 12 : && JS_DefineFunction(cx, obj, "subsessionSnapshot",
1776 : internal_JSKeyedHistogram_SubsessionSnapshot, 1, 0)
1777 12 : && JS_DefineFunction(cx, obj, "snapshotSubsessionAndClear",
1778 : internal_JSKeyedHistogram_SnapshotSubsessionAndClear, 0, 0)
1779 : #endif
1780 12 : && JS_DefineFunction(cx, obj, "keys",
1781 : internal_JSKeyedHistogram_Keys, 0, 0)
1782 18 : && JS_DefineFunction(cx, obj, "clear",
1783 : internal_JSKeyedHistogram_Clear, 0, 0))) {
1784 0 : return NS_ERROR_FAILURE;
1785 : }
1786 :
1787 6 : JS_SetPrivate(obj, h);
1788 6 : ret.setObject(*obj);
1789 6 : return NS_OK;
1790 : }
1791 :
1792 : } // namespace
1793 :
1794 :
1795 : ////////////////////////////////////////////////////////////////////////
1796 : ////////////////////////////////////////////////////////////////////////
1797 : //
1798 : // EXTERNALLY VISIBLE FUNCTIONS in namespace TelemetryHistogram::
1799 :
1800 : // All of these functions are actually in namespace TelemetryHistogram::,
1801 : // but the ::TelemetryHistogram prefix is given explicitly. This is
1802 : // because it is critical to see which calls from these functions are
1803 : // to another function in this interface. Mis-identifying "inwards
1804 : // calls" from "calls to another function in this interface" will lead
1805 : // to deadlocking and/or races. See comments at the top of the file
1806 : // for further (important!) details.
1807 :
1808 : // Create and destroy the singleton StatisticsRecorder object.
1809 3 : void TelemetryHistogram::CreateStatisticsRecorder()
1810 : {
1811 6 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
1812 3 : MOZ_ASSERT(!gStatisticsRecorder);
1813 3 : gStatisticsRecorder = new base::StatisticsRecorder();
1814 3 : }
1815 :
1816 0 : void TelemetryHistogram::DestroyStatisticsRecorder()
1817 : {
1818 0 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
1819 0 : MOZ_ASSERT(gStatisticsRecorder);
1820 0 : if (gStatisticsRecorder) {
1821 0 : delete gStatisticsRecorder;
1822 0 : gStatisticsRecorder = nullptr;
1823 : }
1824 0 : }
1825 :
1826 3 : void TelemetryHistogram::InitializeGlobalState(bool canRecordBase,
1827 : bool canRecordExtended)
1828 : {
1829 6 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
1830 3 : MOZ_ASSERT(!gInitDone, "TelemetryHistogram::InitializeGlobalState "
1831 : "may only be called once");
1832 :
1833 3 : gCanRecordBase = canRecordBase;
1834 3 : gCanRecordExtended = canRecordExtended;
1835 :
1836 : // gHistogramMap should have been pre-sized correctly at the
1837 : // declaration point further up in this file.
1838 :
1839 : // Populate the static histogram name->id cache.
1840 : // Note that the histogram names are statically allocated.
1841 5193 : for (uint32_t i = 0; i < mozilla::Telemetry::HistogramCount; i++) {
1842 5190 : CharPtrEntryType *entry = gHistogramMap.PutEntry(gHistograms[i].id());
1843 5190 : entry->mData = (mozilla::Telemetry::HistogramID) i;
1844 : }
1845 :
1846 : #ifdef DEBUG
1847 3 : gHistogramMap.MarkImmutable();
1848 : #endif
1849 :
1850 3 : mozilla::PodArrayZero(gCorruptHistograms);
1851 :
1852 : // Create registered keyed histograms
1853 5193 : for (const auto & h : gHistograms) {
1854 5190 : if (!h.keyed) {
1855 4809 : continue;
1856 : }
1857 :
1858 762 : const nsDependentCString id(h.id());
1859 762 : const nsDependentCString expiration(h.expiration());
1860 1143 : gKeyedHistograms.Put(id, new KeyedHistogram(id, expiration, h.histogramType,
1861 1143 : h.min, h.max, h.bucketCount, h.dataset));
1862 381 : if (XRE_IsParentProcess()) {
1863 : // We must create registered child keyed histograms as well or else the
1864 : // same code in TelemetrySession.jsm that fails without parent keyed
1865 : // histograms will fail without child keyed histograms.
1866 254 : nsCString contentId(id);
1867 127 : contentId.AppendLiteral(CONTENT_HISTOGRAM_SUFFIX);
1868 : gKeyedHistograms.Put(contentId,
1869 381 : new KeyedHistogram(id, expiration, h.histogramType,
1870 381 : h.min, h.max, h.bucketCount, h.dataset));
1871 :
1872 254 : nsCString gpuId(id);
1873 127 : gpuId.AppendLiteral(GPU_HISTOGRAM_SUFFIX);
1874 : gKeyedHistograms.Put(gpuId,
1875 381 : new KeyedHistogram(id, expiration, h.histogramType,
1876 381 : h.min, h.max, h.bucketCount, h.dataset));
1877 :
1878 254 : nsCString extensionId(id);
1879 127 : extensionId.AppendLiteral(EXTENSION_HISTOGRAM_SUFFIX);
1880 : gKeyedHistograms.Put(extensionId,
1881 381 : new KeyedHistogram(id, expiration, h.histogramType,
1882 381 : h.min, h.max, h.bucketCount, h.dataset));
1883 : }
1884 : }
1885 :
1886 : // Some Telemetry histograms depend on the value of C++ constants and hardcode
1887 : // their values in Histograms.json.
1888 : // We add static asserts here for those values to match so that future changes
1889 : // don't go unnoticed.
1890 : static_assert((JS::gcreason::NUM_TELEMETRY_REASONS + 1) ==
1891 : gHistograms[mozilla::Telemetry::GC_MINOR_REASON].bucketCount &&
1892 : (JS::gcreason::NUM_TELEMETRY_REASONS + 1) ==
1893 : gHistograms[mozilla::Telemetry::GC_MINOR_REASON_LONG].bucketCount &&
1894 : (JS::gcreason::NUM_TELEMETRY_REASONS + 1) ==
1895 : gHistograms[mozilla::Telemetry::GC_REASON_2].bucketCount,
1896 : "NUM_TELEMETRY_REASONS is assumed to be a fixed value in Histograms.json."
1897 : " If this was an intentional change, update the n_values for the "
1898 : "following in Histograms.json: GC_MINOR_REASON, GC_MINOR_REASON_LONG, "
1899 : "GC_REASON_2");
1900 :
1901 : static_assert((mozilla::StartupTimeline::MAX_EVENT_ID + 1) ==
1902 : gHistograms[mozilla::Telemetry::STARTUP_MEASUREMENT_ERRORS].bucketCount,
1903 : "MAX_EVENT_ID is assumed to be a fixed value in Histograms.json. If this"
1904 : " was an intentional change, update the n_values for the following in "
1905 : "Histograms.json: STARTUP_MEASUREMENT_ERRORS");
1906 :
1907 :
1908 3 : gInitDone = true;
1909 3 : }
1910 :
1911 0 : void TelemetryHistogram::DeInitializeGlobalState()
1912 : {
1913 0 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
1914 0 : gCanRecordBase = false;
1915 0 : gCanRecordExtended = false;
1916 0 : gHistogramMap.Clear();
1917 0 : gKeyedHistograms.Clear();
1918 0 : gInitDone = false;
1919 0 : }
1920 :
1921 : #ifdef DEBUG
1922 3 : bool TelemetryHistogram::GlobalStateHasBeenInitialized() {
1923 6 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
1924 6 : return gInitDone;
1925 : }
1926 : #endif
1927 :
1928 : bool
1929 0 : TelemetryHistogram::CanRecordBase() {
1930 0 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
1931 0 : return internal_CanRecordBase();
1932 : }
1933 :
1934 : void
1935 0 : TelemetryHistogram::SetCanRecordBase(bool b) {
1936 0 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
1937 0 : gCanRecordBase = b;
1938 0 : }
1939 :
1940 : bool
1941 0 : TelemetryHistogram::CanRecordExtended() {
1942 0 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
1943 0 : return internal_CanRecordExtended();
1944 : }
1945 :
1946 : void
1947 3 : TelemetryHistogram::SetCanRecordExtended(bool b) {
1948 6 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
1949 3 : gCanRecordExtended = b;
1950 3 : }
1951 :
1952 :
1953 : void
1954 3 : TelemetryHistogram::InitHistogramRecordingEnabled()
1955 : {
1956 6 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
1957 3 : auto processType = XRE_GetProcessType();
1958 5193 : for (size_t i = 0; i < mozilla::ArrayLength(gHistograms); ++i) {
1959 5190 : const HistogramInfo& h = gHistograms[i];
1960 5190 : mozilla::Telemetry::HistogramID id = mozilla::Telemetry::HistogramID(i);
1961 5190 : internal_SetHistogramRecordingEnabled(id,
1962 5190 : CanRecordInProcess(h.record_in_processes,
1963 5190 : processType));
1964 : }
1965 :
1966 12 : for (auto recordingInitiallyDisabledID : kRecordingInitiallyDisabledIDs) {
1967 : internal_SetHistogramRecordingEnabled(recordingInitiallyDisabledID,
1968 9 : false);
1969 : }
1970 3 : }
1971 :
1972 : void
1973 0 : TelemetryHistogram::SetHistogramRecordingEnabled(mozilla::Telemetry::HistogramID aID,
1974 : bool aEnabled)
1975 : {
1976 0 : if (NS_WARN_IF(!internal_IsHistogramEnumId(aID))) {
1977 0 : MOZ_ASSERT_UNREACHABLE("Histogram usage requires valid ids.");
1978 : return;
1979 : }
1980 :
1981 0 : const HistogramInfo& h = gHistograms[aID];
1982 0 : if (!CanRecordInProcess(h.record_in_processes, XRE_GetProcessType())) {
1983 : // Don't permit record_in_process-disabled recording to be re-enabled.
1984 0 : return;
1985 : }
1986 :
1987 0 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
1988 0 : internal_SetHistogramRecordingEnabled(aID, aEnabled);
1989 : }
1990 :
1991 :
1992 : nsresult
1993 0 : TelemetryHistogram::SetHistogramRecordingEnabled(const nsACString &id,
1994 : bool aEnabled)
1995 : {
1996 0 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
1997 :
1998 : mozilla::Telemetry::HistogramID hId;
1999 0 : nsresult rv = internal_GetHistogramEnumId(PromiseFlatCString(id).get(), &hId);
2000 0 : if (NS_FAILED(rv)) {
2001 0 : return rv;
2002 : }
2003 0 : const HistogramInfo& hi = gHistograms[hId];
2004 0 : if (!CanRecordInProcess(hi.record_in_processes, XRE_GetProcessType())) {
2005 0 : return NS_OK;
2006 : }
2007 :
2008 : Histogram *h;
2009 0 : rv = internal_GetHistogramByName(id, &h);
2010 0 : if (NS_SUCCEEDED(rv)) {
2011 0 : h->SetRecordingEnabled(aEnabled);
2012 0 : return NS_OK;
2013 : }
2014 :
2015 0 : KeyedHistogram* keyed = internal_GetKeyedHistogramById(id);
2016 0 : if (keyed) {
2017 0 : keyed->SetRecordingEnabled(aEnabled);
2018 0 : return NS_OK;
2019 : }
2020 :
2021 0 : return NS_ERROR_FAILURE;
2022 : }
2023 :
2024 :
2025 : void
2026 2784 : TelemetryHistogram::Accumulate(mozilla::Telemetry::HistogramID aID,
2027 : uint32_t aSample)
2028 : {
2029 2784 : if (NS_WARN_IF(!internal_IsHistogramEnumId(aID))) {
2030 0 : MOZ_ASSERT_UNREACHABLE("Histogram usage requires valid ids.");
2031 : return;
2032 : }
2033 :
2034 5568 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
2035 2784 : internal_Accumulate(aID, aSample);
2036 2784 : }
2037 :
2038 : void
2039 1284 : TelemetryHistogram::Accumulate(mozilla::Telemetry::HistogramID aID,
2040 : const nsCString& aKey, uint32_t aSample)
2041 : {
2042 1284 : if (NS_WARN_IF(!internal_IsHistogramEnumId(aID))) {
2043 0 : MOZ_ASSERT_UNREACHABLE("Histogram usage requires valid ids.");
2044 : return;
2045 : }
2046 :
2047 2568 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
2048 1284 : internal_Accumulate(aID, aKey, aSample);
2049 1284 : }
2050 :
2051 : void
2052 0 : TelemetryHistogram::Accumulate(const char* name, uint32_t sample)
2053 : {
2054 0 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
2055 0 : if (!internal_CanRecordBase()) {
2056 0 : return;
2057 : }
2058 : mozilla::Telemetry::HistogramID id;
2059 0 : nsresult rv = internal_GetHistogramEnumId(name, &id);
2060 0 : if (NS_FAILED(rv)) {
2061 0 : return;
2062 : }
2063 0 : internal_Accumulate(id, sample);
2064 : }
2065 :
2066 : void
2067 0 : TelemetryHistogram::Accumulate(const char* name,
2068 : const nsCString& key, uint32_t sample)
2069 : {
2070 0 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
2071 0 : if (!internal_CanRecordBase()) {
2072 0 : return;
2073 : }
2074 : mozilla::Telemetry::HistogramID id;
2075 0 : nsresult rv = internal_GetHistogramEnumId(name, &id);
2076 0 : if (NS_SUCCEEDED(rv)) {
2077 0 : internal_Accumulate(id, key, sample);
2078 : }
2079 : }
2080 :
2081 : void
2082 0 : TelemetryHistogram::AccumulateCategorical(mozilla::Telemetry::HistogramID aId,
2083 : const nsCString& label)
2084 : {
2085 0 : if (NS_WARN_IF(!internal_IsHistogramEnumId(aId))) {
2086 0 : MOZ_ASSERT_UNREACHABLE("Histogram usage requires valid ids.");
2087 : return;
2088 : }
2089 :
2090 0 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
2091 0 : if (!internal_CanRecordBase()) {
2092 0 : return;
2093 : }
2094 0 : uint32_t labelId = 0;
2095 0 : if (NS_FAILED(gHistograms[aId].label_id(label.get(), &labelId))) {
2096 0 : return;
2097 : }
2098 0 : internal_Accumulate(aId, labelId);
2099 : }
2100 :
2101 : void
2102 2 : TelemetryHistogram::AccumulateChild(ProcessID aProcessType,
2103 : const nsTArray<Accumulation>& aAccumulations)
2104 : {
2105 2 : MOZ_ASSERT(XRE_IsParentProcess());
2106 :
2107 4 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
2108 2 : if (!internal_CanRecordBase()) {
2109 0 : return;
2110 : }
2111 532 : for (uint32_t i = 0; i < aAccumulations.Length(); ++i) {
2112 530 : if (NS_WARN_IF(!internal_IsHistogramEnumId(aAccumulations[i].mId))) {
2113 0 : MOZ_ASSERT_UNREACHABLE("Histogram usage requires valid ids.");
2114 : continue;
2115 : }
2116 530 : internal_AccumulateChild(aProcessType, aAccumulations[i].mId, aAccumulations[i].mSample);
2117 : }
2118 : }
2119 :
2120 : void
2121 2 : TelemetryHistogram::AccumulateChildKeyed(ProcessID aProcessType,
2122 : const nsTArray<KeyedAccumulation>& aAccumulations)
2123 : {
2124 2 : MOZ_ASSERT(XRE_IsParentProcess());
2125 4 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
2126 2 : if (!internal_CanRecordBase()) {
2127 0 : return;
2128 : }
2129 179 : for (uint32_t i = 0; i < aAccumulations.Length(); ++i) {
2130 177 : if (NS_WARN_IF(!internal_IsHistogramEnumId(aAccumulations[i].mId))) {
2131 0 : MOZ_ASSERT_UNREACHABLE("Histogram usage requires valid ids.");
2132 : continue;
2133 : }
2134 177 : internal_AccumulateChildKeyed(aProcessType,
2135 177 : aAccumulations[i].mId,
2136 177 : aAccumulations[i].mKey,
2137 354 : aAccumulations[i].mSample);
2138 : }
2139 : }
2140 :
2141 : nsresult
2142 9 : TelemetryHistogram::GetHistogramById(const nsACString &name, JSContext *cx,
2143 : JS::MutableHandle<JS::Value> ret)
2144 : {
2145 9 : Histogram *h = nullptr;
2146 : {
2147 18 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
2148 9 : nsresult rv = internal_GetHistogramByName(name, &h);
2149 9 : if (NS_FAILED(rv))
2150 0 : return rv;
2151 : }
2152 : // Runs without protection from |gTelemetryHistogramMutex|
2153 9 : return internal_WrapAndReturnHistogram(h, cx, ret);
2154 : }
2155 :
2156 : nsresult
2157 6 : TelemetryHistogram::GetKeyedHistogramById(const nsACString &name,
2158 : JSContext *cx,
2159 : JS::MutableHandle<JS::Value> ret)
2160 : {
2161 6 : KeyedHistogram* keyed = nullptr;
2162 : {
2163 12 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
2164 6 : if (!gKeyedHistograms.Get(name, &keyed)) {
2165 0 : return NS_ERROR_FAILURE;
2166 : }
2167 : }
2168 : // Runs without protection from |gTelemetryHistogramMutex|
2169 6 : return internal_WrapAndReturnKeyedHistogram(keyed, cx, ret);
2170 : }
2171 :
2172 : const char*
2173 0 : TelemetryHistogram::GetHistogramName(mozilla::Telemetry::HistogramID id)
2174 : {
2175 0 : if (NS_WARN_IF(!internal_IsHistogramEnumId(id))) {
2176 0 : MOZ_ASSERT_UNREACHABLE("Histogram usage requires valid ids.");
2177 : return nullptr;
2178 : }
2179 :
2180 0 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
2181 0 : const HistogramInfo& h = gHistograms[id];
2182 0 : return h.id();
2183 : }
2184 :
2185 : nsresult
2186 0 : TelemetryHistogram::CreateHistogramSnapshots(JSContext *cx,
2187 : JS::MutableHandle<JS::Value> ret,
2188 : bool subsession,
2189 : bool clearSubsession)
2190 : {
2191 : // Runs without protection from |gTelemetryHistogramMutex|
2192 0 : JS::Rooted<JSObject*> root_obj(cx, JS_NewPlainObject(cx));
2193 0 : if (!root_obj)
2194 0 : return NS_ERROR_FAILURE;
2195 0 : ret.setObject(*root_obj);
2196 :
2197 : // Include the GPU process in histogram snapshots only if we actually tried
2198 : // to launch a process for it.
2199 0 : bool includeGPUProcess = false;
2200 0 : if (auto gpm = mozilla::gfx::GPUProcessManager::Get()) {
2201 0 : includeGPUProcess = gpm->AttemptedGPUProcess();
2202 : }
2203 :
2204 : // Ensure that all the HISTOGRAM_FLAG & HISTOGRAM_COUNT histograms have
2205 : // been created, so that their values are snapshotted.
2206 0 : auto processType = XRE_GetProcessType();
2207 0 : for (size_t i = 0; i < mozilla::Telemetry::HistogramCount; ++i) {
2208 0 : const HistogramInfo& hi = gHistograms[i];
2209 0 : if (hi.keyed || !CanRecordInProcess(hi.record_in_processes, processType)) {
2210 0 : continue;
2211 : }
2212 0 : const uint32_t type = hi.histogramType;
2213 0 : if (type == nsITelemetry::HISTOGRAM_FLAG ||
2214 : type == nsITelemetry::HISTOGRAM_COUNT) {
2215 : Histogram *h;
2216 0 : mozilla::DebugOnly<nsresult> rv;
2217 0 : mozilla::Telemetry::HistogramID id = mozilla::Telemetry::HistogramID(i);
2218 :
2219 0 : for (uint32_t process = 0; process < static_cast<uint32_t>(ProcessID::Count); ++process) {
2220 0 : if ((ProcessID(process) == ProcessID::Gpu) && !includeGPUProcess) {
2221 0 : continue;
2222 : }
2223 0 : rv = internal_GetHistogramByEnumId(id, &h, ProcessID(process));
2224 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
2225 : }
2226 : }
2227 : }
2228 :
2229 0 : StatisticsRecorder::Histograms hs;
2230 0 : StatisticsRecorder::GetHistograms(&hs);
2231 :
2232 : // We identify corrupt histograms first, rather than interspersing it
2233 : // in the loop below, to ensure that our corruption statistics don't
2234 : // depend on histogram enumeration order.
2235 : //
2236 : // Of course, we hope that all of these corruption-statistics
2237 : // histograms are not themselves corrupt...
2238 0 : internal_IdentifyCorruptHistograms(hs);
2239 :
2240 : // OK, now we can actually reflect things.
2241 0 : JS::Rooted<JSObject*> hobj(cx);
2242 0 : for (size_t i = 0; i < mozilla::Telemetry::HistogramCount; ++i) {
2243 0 : const HistogramInfo& hi = gHistograms[i];
2244 0 : if (hi.keyed) {
2245 0 : continue;
2246 : }
2247 :
2248 0 : Histogram* h = nullptr;
2249 0 : mozilla::Telemetry::HistogramID id = mozilla::Telemetry::HistogramID(i);
2250 :
2251 0 : for (uint32_t process = 0; process < static_cast<uint32_t>(ProcessID::Count); ++process) {
2252 0 : if (!CanRecordInProcess(hi.record_in_processes, ProcessID(process)) ||
2253 0 : ((ProcessID(process) == ProcessID::Gpu) && !includeGPUProcess)) {
2254 0 : continue;
2255 : }
2256 0 : nsresult rv = internal_GetHistogramByEnumId(id, &h, ProcessID(process));
2257 0 : if (NS_WARN_IF(NS_FAILED(rv)) || !internal_ShouldReflectHistogram(h) ||
2258 0 : internal_IsEmpty(h) || internal_IsExpired(h)) {
2259 0 : continue;
2260 : }
2261 :
2262 0 : Histogram* original = h;
2263 : #if !defined(MOZ_WIDGET_ANDROID)
2264 0 : if (subsession) {
2265 0 : h = internal_GetSubsessionHistogram(*h);
2266 0 : if (!h) {
2267 0 : continue;
2268 : }
2269 : }
2270 : #endif
2271 :
2272 0 : hobj = JS_NewPlainObject(cx);
2273 0 : if (!hobj) {
2274 0 : return NS_ERROR_FAILURE;
2275 : }
2276 0 : switch (internal_ReflectHistogramSnapshot(cx, hobj, h)) {
2277 : case REFLECT_CORRUPT:
2278 : // We can still hit this case even if ShouldReflectHistograms
2279 : // returns true. The histogram lies outside of our control
2280 : // somehow; just skip it.
2281 0 : continue;
2282 : case REFLECT_FAILURE:
2283 0 : return NS_ERROR_FAILURE;
2284 : case REFLECT_OK:
2285 0 : if (!JS_DefineProperty(cx, root_obj, original->histogram_name().c_str(),
2286 : hobj, JSPROP_ENUMERATE)) {
2287 0 : return NS_ERROR_FAILURE;
2288 : }
2289 : }
2290 :
2291 : #if !defined(MOZ_WIDGET_ANDROID)
2292 0 : if (subsession && clearSubsession) {
2293 0 : h->Clear();
2294 : }
2295 : #endif
2296 : }
2297 : }
2298 0 : return NS_OK;
2299 : }
2300 :
2301 : nsresult
2302 0 : TelemetryHistogram::RegisteredHistograms(uint32_t aDataset, uint32_t *aCount,
2303 : char*** aHistograms)
2304 : {
2305 0 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
2306 : return internal_GetRegisteredHistogramIds(false,
2307 0 : aDataset, aCount, aHistograms);
2308 : }
2309 :
2310 : nsresult
2311 0 : TelemetryHistogram::RegisteredKeyedHistograms(uint32_t aDataset,
2312 : uint32_t *aCount,
2313 : char*** aHistograms)
2314 : {
2315 0 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
2316 : return internal_GetRegisteredHistogramIds(true,
2317 0 : aDataset, aCount, aHistograms);
2318 : }
2319 :
2320 : nsresult
2321 0 : TelemetryHistogram::GetKeyedHistogramSnapshots(JSContext *cx,
2322 : JS::MutableHandle<JS::Value> ret)
2323 : {
2324 : // Runs without protection from |gTelemetryHistogramMutex|
2325 0 : JS::Rooted<JSObject*> obj(cx, JS_NewPlainObject(cx));
2326 0 : if (!obj) {
2327 0 : return NS_ERROR_FAILURE;
2328 : }
2329 :
2330 0 : for (auto iter = gKeyedHistograms.Iter(); !iter.Done(); iter.Next()) {
2331 0 : JS::RootedObject snapshot(cx, JS_NewPlainObject(cx));
2332 0 : if (!snapshot) {
2333 0 : return NS_ERROR_FAILURE;
2334 : }
2335 :
2336 0 : if (!NS_SUCCEEDED(iter.Data()->GetJSSnapshot(cx, snapshot, false, false))) {
2337 0 : return NS_ERROR_FAILURE;
2338 : }
2339 :
2340 0 : if (!JS_DefineProperty(cx, obj, PromiseFlatCString(iter.Key()).get(),
2341 : snapshot, JSPROP_ENUMERATE)) {
2342 0 : return NS_ERROR_FAILURE;
2343 : }
2344 : }
2345 :
2346 0 : ret.setObject(*obj);
2347 0 : return NS_OK;
2348 : }
2349 :
2350 : size_t
2351 0 : TelemetryHistogram::GetMapShallowSizesOfExcludingThis(mozilla::MallocSizeOf
2352 : aMallocSizeOf)
2353 : {
2354 0 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
2355 0 : return gHistogramMap.ShallowSizeOfExcludingThis(aMallocSizeOf);
2356 : }
2357 :
2358 : size_t
2359 0 : TelemetryHistogram::GetHistogramSizesofIncludingThis(mozilla::MallocSizeOf
2360 : aMallocSizeOf)
2361 : {
2362 0 : StaticMutexAutoLock locker(gTelemetryHistogramMutex);
2363 0 : StatisticsRecorder::Histograms hs;
2364 0 : StatisticsRecorder::GetHistograms(&hs);
2365 0 : size_t n = 0;
2366 0 : for (auto h : hs) {
2367 0 : n += h->SizeOfIncludingThis(aMallocSizeOf);
2368 : }
2369 0 : return n;
2370 9 : }
|