gecko-dev/toolkit/components/telemetry/Telemetry.h
Jeremy Lempereur bad7346661 Bug 1444735 - Guard AutoCounter and RuntimeAutoCounter against over/under flows. r=chutten
Made sure the telemetry AutoCounter and RuntimeAutoCounter increments are guarded against over/under flows.

MozReview-Commit-ID: 8CZhpKkthoB

--HG--
extra : rebase_source : 4eafb249555128f9bc9fc3ecea13983bfa402aab
2018-03-20 20:04:13 +01:00

599 lines
18 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef Telemetry_h__
#define Telemetry_h__
#include "mozilla/GuardObjects.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/StartupTimeline.h"
#include "nsTArray.h"
#include "nsString.h"
#include "nsXULAppAPI.h"
#include "mozilla/TelemetryHistogramEnums.h"
#include "mozilla/TelemetryScalarEnums.h"
/******************************************************************************
* This implements the Telemetry system.
* It allows recording into histograms as well some more specialized data
* points and gives access to the data.
*
* For documentation on how to add and use new Telemetry probes, see:
* https://developer.mozilla.org/en-US/docs/Mozilla/Performance/Adding_a_new_Telemetry_probe
*
* For more general information on Telemetry see:
* https://wiki.mozilla.org/Telemetry
*****************************************************************************/
namespace mozilla {
namespace HangMonitor {
class HangAnnotations;
} // namespace HangMonitor
namespace Telemetry {
struct HistogramAccumulation;
struct KeyedHistogramAccumulation;
struct ScalarAction;
struct KeyedScalarAction;
struct ChildEventData;
/**
* Initialize the Telemetry service on the main thread at startup.
*/
void Init();
/**
* Adds sample to a histogram defined in TelemetryHistogramEnums.h
*
* @param id - histogram id
* @param sample - value to record.
*/
void Accumulate(HistogramID id, uint32_t sample);
/**
* Adds an array of samples to a histogram defined in TelemetryHistograms.h
* @param id - histogram id
* @param samples - values to record.
*/
void Accumulate(HistogramID id, const nsTArray<uint32_t>& samples);
/**
* Adds sample to a keyed histogram defined in TelemetryHistogramEnums.h
*
* @param id - keyed histogram id
* @param key - the string key
* @param sample - (optional) value to record, defaults to 1.
*/
void Accumulate(HistogramID id, const nsCString& key, uint32_t sample = 1);
/**
* Adds an array of samples to a histogram defined in TelemetryHistograms.h
* @param id - histogram id
* @param samples - values to record.
* @param key - the string key
*/
void Accumulate(HistogramID id, const nsCString& key, const nsTArray<uint32_t>& samples);
/**
* Adds a sample to a histogram defined in TelemetryHistogramEnums.h.
* This function is here to support telemetry measurements from Java,
* where we have only names and not numeric IDs. You should almost
* certainly be using the by-enum-id version instead of this one.
*
* @param name - histogram name
* @param sample - value to record
*/
void Accumulate(const char* name, uint32_t sample);
/**
* Adds a sample to a histogram defined in TelemetryHistogramEnums.h.
* This function is here to support telemetry measurements from Java,
* where we have only names and not numeric IDs. You should almost
* certainly be using the by-enum-id version instead of this one.
*
* @param name - histogram name
* @param key - the string key
* @param sample - sample - (optional) value to record, defaults to 1.
*/
void Accumulate(const char *name, const nsCString& key, uint32_t sample = 1);
/**
* Adds sample to a categorical histogram defined in TelemetryHistogramEnums.h
* This is the typesafe - and preferred - way to use the categorical histograms
* by passing values from the corresponding Telemetry::LABELS_* enum.
*
* @param enumValue - Label value from one of the Telemetry::LABELS_* enums.
*/
template<class E>
void AccumulateCategorical(E enumValue) {
static_assert(IsCategoricalLabelEnum<E>::value,
"Only categorical label enum types are supported.");
Accumulate(static_cast<HistogramID>(CategoricalLabelId<E>::value),
static_cast<uint32_t>(enumValue));
};
/**
* Adds an array of samples to categorical histograms defined in TelemetryHistogramEnums.h
* This is the typesafe - and preferred - way to use the categorical histograms
* by passing values from the corresponding Telemetry::LABELS_* enums.
*
* @param enumValues - Array of labels from Telemetry::LABELS_* enums.
*/
template<class E>
void
AccumulateCategorical(const nsTArray<E>& enumValues)
{
static_assert(IsCategoricalLabelEnum<E>::value,
"Only categorical label enum types are supported.");
nsTArray<uint32_t> intSamples(enumValues.Length());
for (E aValue: enumValues){
intSamples.AppendElement(static_cast<uint32_t>(aValue));
}
HistogramID categoricalId = static_cast<HistogramID>(CategoricalLabelId<E>::value);
Accumulate(categoricalId, intSamples);
}
/**
* Adds sample to a keyed categorical histogram defined in TelemetryHistogramEnums.h
* This is the typesafe - and preferred - way to use the keyed categorical histograms
* by passing values from the corresponding Telemetry::LABELS_* enum.
*
* @param key - the string key
* @param enumValue - Label value from one of the Telemetry::LABELS_* enums.
*/
template<class E>
void AccumulateCategoricalKeyed(const nsCString& key, E enumValue) {
static_assert(IsCategoricalLabelEnum<E>::value,
"Only categorical label enum types are supported.");
Accumulate(static_cast<HistogramID>(CategoricalLabelId<E>::value),
key,
static_cast<uint32_t>(enumValue));
};
/**
* Adds an array of samples to a keyed categorical histogram defined in TelemetryHistogramEnums.h.
* This is the typesafe - and preferred - way to use the keyed categorical histograms
* by passing values from the corresponding Telemetry::LABELS_*enum.
*
* @param key - the string key
* @param enumValue - Label value from one of the Telemetry::LABELS_* enums.
*/
template<class E>
void AccumulateCategoricalKeyed(const nsCString& key, const nsTArray<E>& enumValues) {
static_assert(IsCategoricalLabelEnum<E>::value,
"Only categorical label enum types are supported.");
nsTArray<uint32_t> intSamples(enumValues.Length());
for (E aValue: enumValues){
intSamples.AppendElement(static_cast<uint32_t>(aValue));
}
Accumulate(static_cast<HistogramID>(CategoricalLabelId<E>::value),
key,
intSamples);
};
/**
* Adds sample to a categorical histogram defined in TelemetryHistogramEnums.h
* This string will be matched against the labels defined in Histograms.json.
* If the string does not match a label defined for the histogram, nothing will
* be recorded.
*
* @param id - The histogram id.
* @param label - A string label value that is defined in Histograms.json for this histogram.
*/
void AccumulateCategorical(HistogramID id, const nsCString& label);
/**
* Adds an array of samples to a categorical histogram defined in Histograms.json
*
* @param id - The histogram id
* @param labels - The array of labels to accumulate
*/
void AccumulateCategorical(HistogramID id, const nsTArray<nsCString>& labels);
/**
* Adds time delta in milliseconds to a histogram defined in TelemetryHistogramEnums.h
*
* @param id - histogram id
* @param start - start time
* @param end - end time
*/
void AccumulateTimeDelta(HistogramID id, TimeStamp start, TimeStamp end = TimeStamp::Now());
/**
* Adds time delta in milliseconds to a keyed histogram defined in
* TelemetryHistogramEnums.h
*
* @param id - histogram id
* @param key - the string key
* @param start - start time
* @param end - end time
*/
void
AccumulateTimeDelta(HistogramID id,
const nsCString& key,
TimeStamp start,
TimeStamp end = TimeStamp::Now());
/**
* Enable/disable recording for this histogram in this process at runtime.
* Recording is enabled by default, unless listed at
* kRecordingInitiallyDisabledIDs[]. id must be a valid telemetry enum,
*
* @param id - histogram id
* @param enabled - whether or not to enable recording from now on.
*/
void SetHistogramRecordingEnabled(HistogramID id, bool enabled);
const char* GetHistogramName(HistogramID id);
class MOZ_RAII RuntimeAutoTimer
{
public:
explicit RuntimeAutoTimer(Telemetry::HistogramID aId,
TimeStamp aStart = TimeStamp::Now()
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: id(aId)
, start(aStart)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
explicit RuntimeAutoTimer(Telemetry::HistogramID aId,
const nsCString& aKey,
TimeStamp aStart = TimeStamp::Now()
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: id(aId)
, key(aKey)
, start(aStart)
{
MOZ_ASSERT(!aKey.IsEmpty(), "The key must not be empty.");
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
~RuntimeAutoTimer()
{
if (key.IsEmpty()) {
AccumulateTimeDelta(id, start);
} else {
AccumulateTimeDelta(id, key, start);
}
}
private:
Telemetry::HistogramID id;
const nsCString key;
const TimeStamp start;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
template<HistogramID id>
class MOZ_RAII AutoTimer
{
public:
explicit AutoTimer(TimeStamp aStart = TimeStamp::Now() MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: start(aStart)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
explicit AutoTimer(const nsCString& aKey, TimeStamp aStart = TimeStamp::Now() MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: start(aStart)
, key(aKey)
{
MOZ_ASSERT(!aKey.IsEmpty(), "The key must not be empty.");
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
~AutoTimer() {
if (key.IsEmpty()) {
AccumulateTimeDelta(id, start);
} else {
AccumulateTimeDelta(id, key, start);
}
}
private:
const TimeStamp start;
const nsCString key;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
class MOZ_RAII RuntimeAutoCounter
{
public:
explicit RuntimeAutoCounter(
HistogramID aId,
uint32_t counterStart = 0 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: id(aId)
, counter(counterStart)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
~RuntimeAutoCounter()
{
Accumulate(id, counter);
}
// Prefix increment only, to encourage good habits.
void operator++()
{
if (NS_WARN_IF(counter == std::numeric_limits<uint32_t>::max())) {
return;
}
++counter;
}
// Chaining doesn't make any sense, don't return anything.
void operator+=(int increment)
{
if (NS_WARN_IF(increment > 0 &&
static_cast<uint32_t>(increment) >
(std::numeric_limits<uint32_t>::max() - counter))) {
counter = std::numeric_limits<uint32_t>::max();
return;
}
if (NS_WARN_IF(increment < 0 &&
static_cast<uint32_t>(-increment) > counter)) {
counter = std::numeric_limits<uint32_t>::min();
return;
}
counter += increment;
}
private:
HistogramID id;
uint32_t counter;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
template<HistogramID id>
class MOZ_RAII AutoCounter {
public:
explicit AutoCounter(uint32_t counterStart = 0 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: counter(counterStart)
{
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
~AutoCounter() {
Accumulate(id, counter);
}
// Prefix increment only, to encourage good habits.
void operator++()
{
if (NS_WARN_IF(counter == std::numeric_limits<uint32_t>::max())) {
return;
}
++counter;
}
// Chaining doesn't make any sense, don't return anything.
void operator+=(int increment)
{
if (NS_WARN_IF(increment > 0 &&
static_cast<uint32_t>(increment) >
(std::numeric_limits<uint32_t>::max() - counter))) {
counter = std::numeric_limits<uint32_t>::max();
return;
}
if (NS_WARN_IF(increment < 0 &&
static_cast<uint32_t>(-increment) > counter)) {
counter = std::numeric_limits<uint32_t>::min();
return;
}
counter += increment;
}
private:
uint32_t counter;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
/**
* Indicates whether Telemetry base data recording is turned on. Added for future uses.
*/
bool CanRecordBase();
/**
* Indicates whether Telemetry extended data recording is turned on. This is intended
* to guard calls to Accumulate when the statistic being recorded is expensive to compute.
*/
bool CanRecordExtended();
/**
* Indicates whether Telemetry release data recording is turned on. Usually true.
*
* @see nsITelemetry.canRecordReleaseData
*/
bool CanRecordReleaseData();
/**
* Indicates whether Telemetry pre-release data recording is turned on. Tends
* to be true on pre-release channels.
*
* @see nsITelemetry.canRecordPrereleaseData
*/
bool CanRecordPrereleaseData();
/**
* Records slow SQL statements for Telemetry reporting.
*
* @param statement - offending SQL statement to record
* @param dbName - DB filename
* @param delay - execution time in milliseconds
*/
void RecordSlowSQLStatement(const nsACString &statement,
const nsACString &dbName,
uint32_t delay);
/**
* Record Webrtc ICE candidate type combinations in a 17bit bitmask
*
* @param iceCandidateBitmask - the bitmask representing local and remote ICE
* candidate types present for the connection
* @param success - did the peer connection connected
*/
void
RecordWebrtcIceCandidates(const uint32_t iceCandidateBitmask,
const bool success);
/**
* Initialize I/O Reporting
* Initially this only records I/O for files in the binary directory.
*
* @param aXreDir - XRE directory
*/
void InitIOReporting(nsIFile* aXreDir);
/**
* Set the profile directory. Once called, files in the profile directory will
* be included in I/O reporting. We can't use the directory
* service to obtain this information because it isn't running yet.
*/
void SetProfileDir(nsIFile* aProfD);
/**
* Called to inform Telemetry that startup has completed.
*/
void LeavingStartupStage();
/**
* Called to inform Telemetry that shutdown is commencing.
*/
void EnteringShutdownStage();
/**
* Thresholds for a statement to be considered slow, in milliseconds
*/
const uint32_t kSlowSQLThresholdForMainThread = 50;
const uint32_t kSlowSQLThresholdForHelperThreads = 100;
class ProcessedStack;
/**
* Record the main thread's call stack after it hangs.
*
* @param aDuration - Approximate duration of main thread hang, in seconds
* @param aStack - Array of PCs from the hung call stack
* @param aSystemUptime - System uptime at the time of the hang, in minutes
* @param aFirefoxUptime - Firefox uptime at the time of the hang, in minutes
* @param aAnnotations - Any annotations to be added to the report
*/
#if defined(MOZ_GECKO_PROFILER)
void RecordChromeHang(uint32_t aDuration,
ProcessedStack &aStack,
int32_t aSystemUptime,
int32_t aFirefoxUptime,
mozilla::HangMonitor::HangAnnotations&& aAnnotations);
/**
* Record the current thread's call stack on demand. Note that, the stack is
* only captured once. Subsequent calls result in incrementing the capture
* counter.
*
* @param aKey - A user defined key associated with the captured stack.
*
* NOTE: Unwinding call stacks is an expensive operation performance-wise.
*/
void CaptureStack(const nsCString& aKey);
#endif
/**
* Record a failed attempt at locking the user's profile.
*
* @param aProfileDir The profile directory whose lock attempt failed
*/
void WriteFailedProfileLock(nsIFile* aProfileDir);
/**
* Adds the value to the given scalar.
*
* @param aId The scalar enum id.
* @param aValue The value to add to the scalar.
*/
void ScalarAdd(mozilla::Telemetry::ScalarID aId, uint32_t aValue);
/**
* Sets the scalar to the given value.
*
* @param aId The scalar enum id.
* @param aValue The value to set the scalar to.
*/
void ScalarSet(mozilla::Telemetry::ScalarID aId, uint32_t aValue);
/**
* Sets the scalar to the given value.
*
* @param aId The scalar enum id.
* @param aValue The value to set the scalar to.
*/
void ScalarSet(mozilla::Telemetry::ScalarID aId, bool aValue);
/**
* Sets the scalar to the given value.
*
* @param aId The scalar enum id.
* @param aValue The value to set the scalar to, truncated to
* 50 characters if exceeding that length.
*/
void ScalarSet(mozilla::Telemetry::ScalarID aId, const nsAString& aValue);
/**
* Sets the scalar to the maximum of the current and the passed value.
*
* @param aId The scalar enum id.
* @param aValue The value the scalar is set to if its greater
* than the current value.
*/
void ScalarSetMaximum(mozilla::Telemetry::ScalarID aId, uint32_t aValue);
/**
* Adds the value to the given scalar.
*
* @param aId The scalar enum id.
* @param aKey The scalar key.
* @param aValue The value to add to the scalar.
*/
void ScalarAdd(mozilla::Telemetry::ScalarID aId, const nsAString& aKey, uint32_t aValue);
/**
* Sets the scalar to the given value.
*
* @param aId The scalar enum id.
* @param aKey The scalar key.
* @param aValue The value to set the scalar to.
*/
void ScalarSet(mozilla::Telemetry::ScalarID aId, const nsAString& aKey, uint32_t aValue);
/**
* Sets the scalar to the given value.
*
* @param aId The scalar enum id.
* @param aKey The scalar key.
* @param aValue The value to set the scalar to.
*/
void ScalarSet(mozilla::Telemetry::ScalarID aId, const nsAString& aKey, bool aValue);
/**
* Sets the scalar to the maximum of the current and the passed value.
*
* @param aId The scalar enum id.
* @param aKey The scalar key.
* @param aValue The value the scalar is set to if its greater
* than the current value.
*/
void ScalarSetMaximum(mozilla::Telemetry::ScalarID aId, const nsAString& aKey, uint32_t aValue);
} // namespace Telemetry
} // namespace mozilla
#endif // Telemetry_h__