зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 3 changesets (bug 1302681) for build failures in TelemetryEvent.cpp a=backout
Backed out changeset 9c0bdff48a0a (bug 1302681) Backed out changeset 8e79158a7a1c (bug 1302681) Backed out changeset 7068c8d4448c (bug 1302681) MozReview-Commit-ID: Y7pfsOJRqe
This commit is contained in:
Родитель
442d7cef56
Коммит
807c023ca7
|
@ -35,9 +35,9 @@ struct CommonEventInfo {
|
|||
mozilla::Telemetry::Common::RecordedProcessType record_in_processes;
|
||||
|
||||
// Convenience functions for accessing event strings.
|
||||
const nsCString expiration_version() const;
|
||||
const nsCString category() const;
|
||||
const nsCString extra_key(uint32_t index) const;
|
||||
const char* expiration_version() const;
|
||||
const char* category() const;
|
||||
const char* extra_key(uint32_t index) const;
|
||||
};
|
||||
|
||||
struct EventInfo {
|
||||
|
@ -48,8 +48,8 @@ struct EventInfo {
|
|||
uint32_t method_offset;
|
||||
uint32_t object_offset;
|
||||
|
||||
const nsCString method() const;
|
||||
const nsCString object() const;
|
||||
const char* method() const;
|
||||
const char* object() const;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -19,8 +19,3 @@ extension:
|
|||
gpu:
|
||||
gecko_enum: GeckoProcessType_GPU
|
||||
description: This is the compositor or GPU process.
|
||||
dynamic:
|
||||
gecko_enum: GeckoProcessType_Default
|
||||
description: >
|
||||
This is not a real process, it is used to logically group add-on probes.
|
||||
It contains data of any probes registered at runtime by add-ons.
|
||||
|
|
|
@ -1807,20 +1807,12 @@ TelemetryImpl::RecordEvent(const nsACString & aCategory, const nsACString & aMet
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelemetryImpl::SnapshotEvents(uint32_t aDataset, bool aClear, JSContext* aCx,
|
||||
TelemetryImpl::SnapshotBuiltinEvents(uint32_t aDataset, bool aClear, JSContext* aCx,
|
||||
uint8_t optional_argc, JS::MutableHandleValue aResult)
|
||||
{
|
||||
return TelemetryEvent::CreateSnapshots(aDataset, aClear, aCx, optional_argc, aResult);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelemetryImpl::RegisterEvents(const nsACString& aCategory,
|
||||
JS::Handle<JS::Value> aEventData,
|
||||
JSContext* cx)
|
||||
{
|
||||
return TelemetryEvent::RegisterEvents(aCategory, aEventData, cx);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelemetryImpl::ClearEvents()
|
||||
{
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include <prtime.h>
|
||||
#include <limits>
|
||||
#include "nsITelemetry.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "nsDataHashtable.h"
|
||||
|
@ -20,7 +19,6 @@
|
|||
#include "nsJSUtils.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "nsUTF8Utils.h"
|
||||
#include "nsPrintfCString.h"
|
||||
|
||||
#include "TelemetryCommon.h"
|
||||
#include "TelemetryEvent.h"
|
||||
|
@ -91,10 +89,9 @@ namespace {
|
|||
|
||||
const uint32_t kEventCount = mozilla::Telemetry::EventID::EventCount;
|
||||
// This is a special event id used to mark expired events, to make expiry checks
|
||||
// cheap at runtime.
|
||||
const uint32_t kExpiredEventId = std::numeric_limits<uint32_t>::max();
|
||||
static_assert(kExpiredEventId > kEventCount,
|
||||
"Built-in event count should be less than the expired event id.");
|
||||
// faster at runtime.
|
||||
const uint32_t kExpiredEventId = kEventCount + 1;
|
||||
static_assert(kEventCount < kExpiredEventId, "Should not overflow.");
|
||||
|
||||
// This is the hard upper limit on the number of event records we keep in storage.
|
||||
// If we cross this limit, we will drop any further event recording until elements
|
||||
|
@ -104,60 +101,10 @@ const uint32_t kMaxEventRecords = 1000;
|
|||
const uint32_t kMaxValueByteLength = 80;
|
||||
// Maximum length of any string value in the extra dictionary, in UTF8 byte sequence length.
|
||||
const uint32_t kMaxExtraValueByteLength = 80;
|
||||
// Maximum length of dynamic method names, in UTF8 byte sequence length.
|
||||
const uint32_t kMaxMethodNameByteLength = 20;
|
||||
// Maximum length of dynamic object names, in UTF8 byte sequence length.
|
||||
const uint32_t kMaxObjectNameByteLength = 20;
|
||||
// Maximum length of extra key names, in UTF8 byte sequence length.
|
||||
const uint32_t kMaxExtraKeyNameByteLength = 15;
|
||||
// The maximum number of valid extra keys for an event.
|
||||
const uint32_t kMaxExtraKeyCount = 10;
|
||||
|
||||
typedef nsDataHashtable<nsCStringHashKey, uint32_t> StringUintMap;
|
||||
typedef nsClassHashtable<nsCStringHashKey, nsCString> StringMap;
|
||||
|
||||
struct EventKey {
|
||||
uint32_t id;
|
||||
bool dynamic;
|
||||
};
|
||||
|
||||
struct DynamicEventInfo {
|
||||
DynamicEventInfo(const nsACString& category, const nsACString& method,
|
||||
const nsACString& object, const nsTArray<nsCString>& extra_keys,
|
||||
bool recordOnRelease)
|
||||
: category(category)
|
||||
, method(method)
|
||||
, object(object)
|
||||
, extra_keys(extra_keys)
|
||||
, recordOnRelease(recordOnRelease)
|
||||
{}
|
||||
|
||||
DynamicEventInfo(const DynamicEventInfo&) = default;
|
||||
DynamicEventInfo& operator=(const DynamicEventInfo&) = delete;
|
||||
|
||||
const nsCString category;
|
||||
const nsCString method;
|
||||
const nsCString object;
|
||||
const nsTArray<nsCString> extra_keys;
|
||||
const bool recordOnRelease;
|
||||
|
||||
size_t
|
||||
SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
|
||||
{
|
||||
size_t n = 0;
|
||||
|
||||
n += category.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
|
||||
n += method.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
|
||||
n += object.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
|
||||
n += extra_keys.ShallowSizeOfExcludingThis(aMallocSizeOf);
|
||||
for (auto& key : extra_keys) {
|
||||
n += key.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
};
|
||||
|
||||
enum class RecordEventResult {
|
||||
Ok,
|
||||
UnknownEvent,
|
||||
|
@ -167,29 +114,29 @@ enum class RecordEventResult {
|
|||
WrongProcess,
|
||||
};
|
||||
|
||||
enum class RegisterEventResult {
|
||||
Ok,
|
||||
AlreadyRegistered,
|
||||
};
|
||||
|
||||
typedef nsTArray<EventExtraEntry> ExtraArray;
|
||||
|
||||
class EventRecord {
|
||||
public:
|
||||
EventRecord(double timestamp, const EventKey& key, const Maybe<nsCString>& value,
|
||||
EventRecord(double timestamp, uint32_t eventId, const Maybe<nsCString>& value,
|
||||
const ExtraArray& extra)
|
||||
: mTimestamp(timestamp)
|
||||
, mEventKey(key)
|
||||
, mEventId(eventId)
|
||||
, mValue(value)
|
||||
, mExtra(extra)
|
||||
{}
|
||||
|
||||
EventRecord(const EventRecord& other) = default;
|
||||
EventRecord(const EventRecord& other)
|
||||
: mTimestamp(other.mTimestamp)
|
||||
, mEventId(other.mEventId)
|
||||
, mValue(other.mValue)
|
||||
, mExtra(other.mExtra)
|
||||
{}
|
||||
|
||||
EventRecord& operator=(const EventRecord& other) = delete;
|
||||
|
||||
double Timestamp() const { return mTimestamp; }
|
||||
const EventKey& GetEventKey() const { return mEventKey; }
|
||||
uint32_t EventId() const { return mEventId; }
|
||||
const Maybe<nsCString>& Value() const { return mValue; }
|
||||
const ExtraArray& Extra() const { return mExtra; }
|
||||
|
||||
|
@ -197,43 +144,43 @@ public:
|
|||
|
||||
private:
|
||||
const double mTimestamp;
|
||||
const EventKey mEventKey;
|
||||
const uint32_t mEventId;
|
||||
const Maybe<nsCString> mValue;
|
||||
const ExtraArray mExtra;
|
||||
};
|
||||
|
||||
// Implements the methods for EventInfo.
|
||||
const nsCString
|
||||
const char*
|
||||
EventInfo::method() const
|
||||
{
|
||||
return nsCString(&gEventsStringTable[this->method_offset]);
|
||||
return &gEventsStringTable[this->method_offset];
|
||||
}
|
||||
|
||||
const nsCString
|
||||
const char*
|
||||
EventInfo::object() const
|
||||
{
|
||||
return nsCString(&gEventsStringTable[this->object_offset]);
|
||||
return &gEventsStringTable[this->object_offset];
|
||||
}
|
||||
|
||||
// Implements the methods for CommonEventInfo.
|
||||
const nsCString
|
||||
const char*
|
||||
CommonEventInfo::category() const
|
||||
{
|
||||
return nsCString(&gEventsStringTable[this->category_offset]);
|
||||
return &gEventsStringTable[this->category_offset];
|
||||
}
|
||||
|
||||
const nsCString
|
||||
const char*
|
||||
CommonEventInfo::expiration_version() const
|
||||
{
|
||||
return nsCString(&gEventsStringTable[this->expiration_version_offset]);
|
||||
return &gEventsStringTable[this->expiration_version_offset];
|
||||
}
|
||||
|
||||
const nsCString
|
||||
const char*
|
||||
CommonEventInfo::extra_key(uint32_t index) const
|
||||
{
|
||||
MOZ_ASSERT(index < this->extra_count);
|
||||
uint32_t key_index = gExtraKeysTable[this->extra_index + index];
|
||||
return nsCString(&gEventsStringTable[key_index]);
|
||||
return &gEventsStringTable[key_index];
|
||||
}
|
||||
|
||||
// Implementation for the EventRecord class.
|
||||
|
@ -270,17 +217,9 @@ UniqueEventName(const nsACString& category, const nsACString& method, const nsAC
|
|||
nsCString
|
||||
UniqueEventName(const EventInfo& info)
|
||||
{
|
||||
return UniqueEventName(info.common_info.category(),
|
||||
info.method(),
|
||||
info.object());
|
||||
}
|
||||
|
||||
nsCString
|
||||
UniqueEventName(const DynamicEventInfo& info)
|
||||
{
|
||||
return UniqueEventName(info.category,
|
||||
info.method,
|
||||
info.object);
|
||||
return UniqueEventName(nsDependentCString(info.common_info.category()),
|
||||
nsDependentCString(info.method()),
|
||||
nsDependentCString(info.object()));
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -316,23 +255,20 @@ bool gInitDone = false;
|
|||
bool gCanRecordBase;
|
||||
bool gCanRecordExtended;
|
||||
|
||||
// The EventName -> EventKey cache map.
|
||||
nsClassHashtable<nsCStringHashKey, EventKey> gEventNameIDMap(kEventCount);
|
||||
// The EventName -> EventID cache map.
|
||||
StringUintMap gEventNameIDMap(kEventCount);
|
||||
|
||||
// The CategoryName -> CategoryID cache map.
|
||||
StringUintMap gCategoryNameIDMap;
|
||||
|
||||
// This tracks the IDs of the categories for which recording is enabled.
|
||||
nsTHashtable<nsCStringHashKey> gEnabledCategories;
|
||||
nsTHashtable<nsUint32HashKey> gEnabledCategories;
|
||||
|
||||
// The main event storage. Events are inserted here, keyed by process id and
|
||||
// in recording order.
|
||||
typedef nsTArray<EventRecord> EventRecordArray;
|
||||
nsClassHashtable<nsUint32HashKey, EventRecordArray> gEventRecords;
|
||||
|
||||
// The details on dynamic events that are recorded from addons are registered here.
|
||||
nsTArray<DynamicEventInfo> gDynamicEventInfo;
|
||||
|
||||
} // namespace
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
@ -342,60 +278,27 @@ nsTArray<DynamicEventInfo> gDynamicEventInfo;
|
|||
|
||||
namespace {
|
||||
|
||||
unsigned int
|
||||
GetDataset(const StaticMutexAutoLock& lock, const EventKey& eventKey)
|
||||
{
|
||||
if (!eventKey.dynamic) {
|
||||
return gEventInfo[eventKey.id].common_info.dataset;
|
||||
}
|
||||
|
||||
return gDynamicEventInfo[eventKey.id].recordOnRelease ?
|
||||
nsITelemetry::DATASET_RELEASE_CHANNEL_OPTOUT :
|
||||
nsITelemetry::DATASET_RELEASE_CHANNEL_OPTIN;
|
||||
}
|
||||
|
||||
nsCString
|
||||
GetCategory(const StaticMutexAutoLock& lock, const EventKey& eventKey)
|
||||
{
|
||||
if (!eventKey.dynamic) {
|
||||
return gEventInfo[eventKey.id].common_info.category();
|
||||
}
|
||||
|
||||
return gDynamicEventInfo[eventKey.id].category;
|
||||
}
|
||||
|
||||
bool
|
||||
CanRecordEvent(const StaticMutexAutoLock& lock, const EventKey& eventKey,
|
||||
CanRecordEvent(const StaticMutexAutoLock& lock, const CommonEventInfo& info,
|
||||
ProcessID process)
|
||||
{
|
||||
if (!gCanRecordBase) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CanRecordDataset(GetDataset(lock, eventKey), gCanRecordBase, gCanRecordExtended)) {
|
||||
if (!CanRecordDataset(info.dataset, gCanRecordBase, gCanRecordExtended)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We don't allow specifying a process to record in for dynamic events.
|
||||
if (!eventKey.dynamic) {
|
||||
const CommonEventInfo& info = gEventInfo[eventKey.id].common_info;
|
||||
if (!CanRecordInProcess(info.record_in_processes, process)) {
|
||||
return false;
|
||||
}
|
||||
if (!CanRecordInProcess(info.record_in_processes, process)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return gEnabledCategories.GetEntry(GetCategory(lock, eventKey));
|
||||
}
|
||||
|
||||
bool
|
||||
IsExpired(const EventKey& key)
|
||||
{
|
||||
return key.id == kExpiredEventId;
|
||||
return gEnabledCategories.GetEntry(info.category_offset);
|
||||
}
|
||||
|
||||
EventRecordArray*
|
||||
GetEventRecordsForProcess(const StaticMutexAutoLock& lock, ProcessID processType,
|
||||
const EventKey& eventKey)
|
||||
GetEventRecordsForProcess(const StaticMutexAutoLock& lock, ProcessID processType)
|
||||
{
|
||||
EventRecordArray* eventRecords = nullptr;
|
||||
if (!gEventRecords.Get(uint32_t(processType), &eventRecords)) {
|
||||
|
@ -405,41 +308,14 @@ GetEventRecordsForProcess(const StaticMutexAutoLock& lock, ProcessID processType
|
|||
return eventRecords;
|
||||
}
|
||||
|
||||
EventKey*
|
||||
GetEventKey(const StaticMutexAutoLock& lock, const nsACString& category,
|
||||
const nsACString& method, const nsACString& object)
|
||||
bool
|
||||
GetEventId(const StaticMutexAutoLock& lock, const nsACString& category,
|
||||
const nsACString& method, const nsACString& object,
|
||||
uint32_t* eventId)
|
||||
{
|
||||
EventKey* event;
|
||||
MOZ_ASSERT(eventId);
|
||||
const nsCString& name = UniqueEventName(category, method, object);
|
||||
if (!gEventNameIDMap.Get(name, &event)) {
|
||||
return nullptr;
|
||||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckExtraKeysValid(const EventKey& eventKey, const ExtraArray& extra)
|
||||
{
|
||||
nsTHashtable<nsCStringHashKey> validExtraKeys;
|
||||
if (!eventKey.dynamic) {
|
||||
const CommonEventInfo& common = gEventInfo[eventKey.id].common_info;
|
||||
for (uint32_t i = 0; i < common.extra_count; ++i) {
|
||||
validExtraKeys.PutEntry(common.extra_key(i));
|
||||
}
|
||||
} else {
|
||||
const DynamicEventInfo& info = gDynamicEventInfo[eventKey.id];
|
||||
for (uint32_t i = 0, len = info.extra_keys.Length(); i < len; ++i) {
|
||||
validExtraKeys.PutEntry(info.extra_keys[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < extra.Length(); ++i) {
|
||||
if (!validExtraKeys.GetEntry(extra[i].key)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return gEventNameIDMap.Get(name, eventId);
|
||||
}
|
||||
|
||||
RecordEventResult
|
||||
|
@ -448,43 +324,47 @@ RecordEvent(const StaticMutexAutoLock& lock, ProcessID processType,
|
|||
const nsACString& method, const nsACString& object,
|
||||
const Maybe<nsCString>& value, const ExtraArray& extra)
|
||||
{
|
||||
// Look up the event id.
|
||||
EventKey* eventKey = GetEventKey(lock, category, method, object);
|
||||
if (!eventKey) {
|
||||
return RecordEventResult::UnknownEvent;
|
||||
}
|
||||
|
||||
if (eventKey->dynamic) {
|
||||
processType = ProcessID::Dynamic;
|
||||
}
|
||||
|
||||
EventRecordArray* eventRecords = GetEventRecordsForProcess(lock, processType, *eventKey);
|
||||
EventRecordArray* eventRecords = GetEventRecordsForProcess(lock, processType);
|
||||
|
||||
// Apply hard limit on event count in storage.
|
||||
if (eventRecords->Length() >= kMaxEventRecords) {
|
||||
return RecordEventResult::StorageLimitReached;
|
||||
}
|
||||
|
||||
// Look up the event id.
|
||||
uint32_t eventId;
|
||||
if (!GetEventId(lock, category, method, object, &eventId)) {
|
||||
return RecordEventResult::UnknownEvent;
|
||||
}
|
||||
|
||||
// If the event is expired or not enabled for this process, we silently drop this call.
|
||||
// We don't want recording for expired probes to be an error so code doesn't
|
||||
// have to be removed at a specific time or version.
|
||||
// Even logging warnings would become very noisy.
|
||||
if (IsExpired(*eventKey)) {
|
||||
if (eventId == kExpiredEventId) {
|
||||
return RecordEventResult::ExpiredEvent;
|
||||
}
|
||||
|
||||
// Check whether we can record this event.
|
||||
if (!CanRecordEvent(lock, *eventKey, processType)) {
|
||||
const CommonEventInfo& common = gEventInfo[eventId].common_info;
|
||||
if (!CanRecordEvent(lock, common, processType)) {
|
||||
return RecordEventResult::Ok;
|
||||
}
|
||||
|
||||
// Check whether the extra keys passed are valid.
|
||||
if (!CheckExtraKeysValid(*eventKey, extra)) {
|
||||
return RecordEventResult::InvalidExtraKey;
|
||||
nsTHashtable<nsCStringHashKey> validExtraKeys;
|
||||
for (uint32_t i = 0; i < common.extra_count; ++i) {
|
||||
validExtraKeys.PutEntry(nsDependentCString(common.extra_key(i)));
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < extra.Length(); ++i) {
|
||||
if (!validExtraKeys.GetEntry(extra[i].key)) {
|
||||
return RecordEventResult::InvalidExtraKey;
|
||||
}
|
||||
}
|
||||
|
||||
// Add event record.
|
||||
eventRecords->AppendElement(EventRecord(timestamp, *eventKey, value, extra));
|
||||
eventRecords->AppendElement(EventRecord(timestamp, eventId, value, extra));
|
||||
return RecordEventResult::Ok;
|
||||
}
|
||||
|
||||
|
@ -492,18 +372,16 @@ RecordEventResult
|
|||
ShouldRecordChildEvent(const StaticMutexAutoLock& lock, const nsACString& category,
|
||||
const nsACString& method, const nsACString& object)
|
||||
{
|
||||
EventKey* eventKey = GetEventKey(lock, category, method, object);
|
||||
if (!eventKey) {
|
||||
// This event is unknown in this process, but it might be a dynamic event
|
||||
// that was registered in the parent process.
|
||||
return RecordEventResult::Ok;
|
||||
uint32_t eventId;
|
||||
if (!GetEventId(lock, category, method, object, &eventId)) {
|
||||
return RecordEventResult::UnknownEvent;
|
||||
}
|
||||
|
||||
if (IsExpired(*eventKey)) {
|
||||
if (eventId == kExpiredEventId) {
|
||||
return RecordEventResult::ExpiredEvent;
|
||||
}
|
||||
|
||||
const auto processes = gEventInfo[eventKey->id].common_info.record_in_processes;
|
||||
const auto processes = gEventInfo[eventId].common_info.record_in_processes;
|
||||
if (!CanRecordInProcess(processes, XRE_GetProcessType())) {
|
||||
return RecordEventResult::WrongProcess;
|
||||
}
|
||||
|
@ -511,33 +389,6 @@ ShouldRecordChildEvent(const StaticMutexAutoLock& lock, const nsACString& catego
|
|||
return RecordEventResult::Ok;
|
||||
}
|
||||
|
||||
RegisterEventResult
|
||||
RegisterEvents(const StaticMutexAutoLock& lock, const nsACString& category,
|
||||
const nsTArray<DynamicEventInfo>& eventInfos,
|
||||
const nsTArray<bool>& eventExpired)
|
||||
{
|
||||
MOZ_ASSERT(eventInfos.Length() == eventExpired.Length(), "Event data array sizes should match.");
|
||||
|
||||
// Check that none of the events are already registered.
|
||||
for (auto& info : eventInfos) {
|
||||
if (gEventNameIDMap.Get(UniqueEventName(info))) {
|
||||
return RegisterEventResult::AlreadyRegistered;
|
||||
}
|
||||
}
|
||||
|
||||
// Register the new events.
|
||||
for (uint32_t i = 0, len = eventInfos.Length(); i < len; ++i) {
|
||||
gDynamicEventInfo.AppendElement(eventInfos[i]);
|
||||
uint32_t eventId = eventExpired[i] ? kExpiredEventId : gDynamicEventInfo.Length() - 1;
|
||||
gEventNameIDMap.Put(UniqueEventName(eventInfos[i]), new EventKey{eventId, true});
|
||||
}
|
||||
|
||||
// Now after successful registration enable recording for this category.
|
||||
gEnabledCategories.PutEntry(category);
|
||||
|
||||
return RegisterEventResult::Ok;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
@ -550,8 +401,7 @@ namespace {
|
|||
nsresult
|
||||
SerializeEventsArray(const EventRecordArray& events,
|
||||
JSContext* cx,
|
||||
JS::MutableHandleObject result,
|
||||
unsigned int dataset)
|
||||
JS::MutableHandleObject result)
|
||||
{
|
||||
// We serialize the events to a JS array.
|
||||
JS::RootedObject eventsArray(cx, JS_NewArrayObject(cx, events.Length()));
|
||||
|
@ -561,6 +411,7 @@ SerializeEventsArray(const EventRecordArray& events,
|
|||
|
||||
for (uint32_t i = 0; i < events.Length(); ++i) {
|
||||
const EventRecord& record = events[i];
|
||||
const EventInfo& info = gEventInfo[record.EventId()];
|
||||
|
||||
// Each entry is an array of one of the forms:
|
||||
// [timestamp, category, method, object, value]
|
||||
|
@ -575,21 +426,12 @@ SerializeEventsArray(const EventRecordArray& events,
|
|||
}
|
||||
|
||||
// Add category, method, object.
|
||||
nsCString strings[3];
|
||||
const EventKey& eventKey = record.GetEventKey();
|
||||
if (!eventKey.dynamic) {
|
||||
const EventInfo& info = gEventInfo[eventKey.id];
|
||||
strings[0] = info.common_info.category();
|
||||
strings[1] = info.method();
|
||||
strings[2] = info.object();
|
||||
} else {
|
||||
const DynamicEventInfo& info = gDynamicEventInfo[eventKey.id];
|
||||
strings[0] = info.category;
|
||||
strings[1] = info.method;
|
||||
strings[2] = info.object;
|
||||
}
|
||||
|
||||
for (const nsCString& s : strings) {
|
||||
const char* strings[] = {
|
||||
info.common_info.category(),
|
||||
info.method(),
|
||||
info.object(),
|
||||
};
|
||||
for (const char* s : strings) {
|
||||
const NS_ConvertUTF8toUTF16 wide(s);
|
||||
if (!items.append(JS::StringValue(JS_NewUCStringCopyN(cx, wide.Data(), wide.Length())))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
|
@ -687,19 +529,20 @@ TelemetryEvent::InitializeGlobalState(bool aCanRecordBase, bool aCanRecordExtend
|
|||
// If this event is expired or not recorded in this process, mark it with
|
||||
// a special event id.
|
||||
// This avoids doing repeated checks at runtime.
|
||||
if (IsExpiredVersion(info.common_info.expiration_version().get()) ||
|
||||
if (IsExpiredVersion(info.common_info.expiration_version()) ||
|
||||
IsExpiredDate(info.common_info.expiration_day)) {
|
||||
eventId = kExpiredEventId;
|
||||
}
|
||||
|
||||
gEventNameIDMap.Put(UniqueEventName(info), new EventKey{eventId, false});
|
||||
if (!gCategoryNameIDMap.Contains(info.common_info.category())) {
|
||||
gCategoryNameIDMap.Put(info.common_info.category(),
|
||||
gEventNameIDMap.Put(UniqueEventName(info), eventId);
|
||||
if (!gCategoryNameIDMap.Contains(nsDependentCString(info.common_info.category()))) {
|
||||
gCategoryNameIDMap.Put(nsDependentCString(info.common_info.category()),
|
||||
info.common_info.category_offset);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
gEventNameIDMap.MarkImmutable();
|
||||
gCategoryNameIDMap.MarkImmutable();
|
||||
#endif
|
||||
gInitDone = true;
|
||||
|
@ -876,14 +719,10 @@ TelemetryEvent::RecordEvent(const nsACString& aCategory, const nsACString& aMeth
|
|||
PromiseFlatCString(aObject).get());
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
case RecordEventResult::InvalidExtraKey: {
|
||||
nsPrintfCString msg(R"(Invalid extra key for event ["%s", "%s", "%s"].)",
|
||||
PromiseFlatCString(aCategory).get(),
|
||||
PromiseFlatCString(aMethod).get(),
|
||||
PromiseFlatCString(aObject).get());
|
||||
LogToBrowserConsole(nsIScriptError::warningFlag, NS_ConvertUTF8toUTF16(msg));
|
||||
case RecordEventResult::InvalidExtraKey:
|
||||
LogToBrowserConsole(nsIScriptError::warningFlag,
|
||||
NS_LITERAL_STRING("Invalid extra key for event."));
|
||||
return NS_OK;
|
||||
}
|
||||
case RecordEventResult::StorageLimitReached:
|
||||
LogToBrowserConsole(nsIScriptError::warningFlag,
|
||||
NS_LITERAL_STRING("Event storage limit reached."));
|
||||
|
@ -893,223 +732,6 @@ TelemetryEvent::RecordEvent(const nsACString& aCategory, const nsACString& aMeth
|
|||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
GetArrayPropertyValues(JSContext* cx, JS::HandleObject obj, const char* property,
|
||||
nsTArray<nsCString>* results)
|
||||
{
|
||||
JS::RootedValue value(cx);
|
||||
if (!JS_GetProperty(cx, obj, property, &value)) {
|
||||
JS_ReportErrorASCII(cx, R"(Missing required property "%s" for event)", property);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isArray = false;
|
||||
if (!JS_IsArrayObject(cx, value, &isArray) || !isArray) {
|
||||
JS_ReportErrorASCII(cx, R"(Property "%s" for event should be an array)", property);
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::RootedObject arrayObj(cx, &value.toObject());
|
||||
uint32_t arrayLength;
|
||||
if (!JS_GetArrayLength(cx, arrayObj, &arrayLength)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t arrayIdx = 0; arrayIdx < arrayLength; ++arrayIdx) {
|
||||
JS::Rooted<JS::Value> element(cx);
|
||||
if (!JS_GetElement(cx, arrayObj, arrayIdx, &element)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!element.isString()) {
|
||||
JS_ReportErrorASCII(cx, R"(Array entries for event property "%s" should be strings)", property);
|
||||
return false;
|
||||
}
|
||||
|
||||
nsAutoJSString jsStr;
|
||||
if (!jsStr.init(cx, element)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
results->AppendElement(NS_ConvertUTF16toUTF8(jsStr));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsStringCharValid(const char aChar, const bool allowInfixPeriod)
|
||||
{
|
||||
return (aChar >= 'A' && aChar <= 'Z')
|
||||
|| (aChar >= 'a' && aChar <= 'z')
|
||||
|| (aChar >= '0' && aChar <= '9')
|
||||
|| (allowInfixPeriod && (aChar == '.'));
|
||||
}
|
||||
|
||||
static bool
|
||||
IsValidIdentifierString(const nsACString& str, const size_t maxLength,
|
||||
const bool allowInfixPeriod)
|
||||
{
|
||||
// Check string length.
|
||||
if (str.Length() > maxLength) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check string characters.
|
||||
const char* first = str.BeginReading();
|
||||
const char* end = str.EndReading();
|
||||
|
||||
for (const char* cur = first; cur < end; ++cur) {
|
||||
const bool allowPeriod = allowInfixPeriod && (cur != first) && (cur != (end - 1));
|
||||
if (!IsStringCharValid(*cur, allowPeriod)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult
|
||||
TelemetryEvent::RegisterEvents(const nsACString& aCategory,
|
||||
JS::Handle<JS::Value> aEventData,
|
||||
JSContext* cx)
|
||||
{
|
||||
if (!IsValidIdentifierString(aCategory, 30, true)) {
|
||||
JS_ReportErrorASCII(cx, "Category parameter should match the identifier pattern.");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (!aEventData.isObject()) {
|
||||
JS_ReportErrorASCII(cx, "Event data parameter should be an object");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
JS::RootedObject obj(cx, &aEventData.toObject());
|
||||
JS::Rooted<JS::IdVector> eventPropertyIds(cx, JS::IdVector(cx));
|
||||
if (!JS_Enumerate(cx, obj, &eventPropertyIds)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Collect the event data into local storage first.
|
||||
// Only after successfully validating all contained events will we register them into global storage.
|
||||
nsTArray<DynamicEventInfo> newEventInfos;
|
||||
nsTArray<bool> newEventExpired;
|
||||
|
||||
for (size_t i = 0, n = eventPropertyIds.length(); i < n; i++) {
|
||||
nsAutoJSString eventName;
|
||||
if (!eventName.init(cx, eventPropertyIds[i])) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!IsValidIdentifierString(NS_ConvertUTF16toUTF8(eventName), kMaxMethodNameByteLength, false)) {
|
||||
JS_ReportErrorASCII(cx, "Event names should match the identifier pattern.");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
JS::RootedValue value(cx);
|
||||
if (!JS_GetPropertyById(cx, obj, eventPropertyIds[i], &value) || !value.isObject()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
JS::RootedObject eventObj(cx, &value.toObject());
|
||||
|
||||
// Extract the event registration data.
|
||||
nsTArray<nsCString> methods;
|
||||
nsTArray<nsCString> objects;
|
||||
nsTArray<nsCString> extra_keys;
|
||||
bool expired = false;
|
||||
bool recordOnRelease = false;
|
||||
|
||||
// The methods & objects properties are required.
|
||||
if (!GetArrayPropertyValues(cx, eventObj, "methods", &methods)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!GetArrayPropertyValues(cx, eventObj, "objects", &objects)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// extra_keys is optional.
|
||||
bool hasProperty = false;
|
||||
if (JS_HasProperty(cx, eventObj, "extra_keys", &hasProperty) && hasProperty) {
|
||||
if (!GetArrayPropertyValues(cx, eventObj, "extra_keys", &extra_keys)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
// expired is optional.
|
||||
if (JS_HasProperty(cx, eventObj, "expired", &hasProperty) && hasProperty) {
|
||||
JS::RootedValue temp(cx);
|
||||
if (!JS_GetProperty(cx, eventObj, "expired", &temp) || !temp.isBoolean()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
expired = temp.toBoolean();
|
||||
}
|
||||
|
||||
// record_on_release is optional.
|
||||
if (JS_HasProperty(cx, eventObj, "record_on_release", &hasProperty) && hasProperty) {
|
||||
JS::RootedValue temp(cx);
|
||||
if (!JS_GetProperty(cx, eventObj, "record_on_release", &temp) || !temp.isBoolean()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
recordOnRelease = temp.toBoolean();
|
||||
}
|
||||
|
||||
// Validate methods.
|
||||
for (auto& method : methods) {
|
||||
if (!IsValidIdentifierString(method, kMaxMethodNameByteLength, false)) {
|
||||
JS_ReportErrorASCII(cx, "Method names should match the identifier pattern.");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate objects.
|
||||
for (auto& object : objects) {
|
||||
if (!IsValidIdentifierString(object, kMaxObjectNameByteLength, false)) {
|
||||
JS_ReportErrorASCII(cx, "Object names should match the identifier pattern.");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate extra keys.
|
||||
if (extra_keys.Length() > kMaxExtraKeyCount) {
|
||||
JS_ReportErrorASCII(cx, "No more than 10 extra keys can be registered.");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
for (auto& key : extra_keys) {
|
||||
if (!IsValidIdentifierString(key, kMaxExtraKeyNameByteLength, false)) {
|
||||
JS_ReportErrorASCII(cx, "Extra key names should match the identifier pattern.");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
// Append event infos to be registered.
|
||||
for (auto& method : methods) {
|
||||
for (auto& object : objects) {
|
||||
// We defer the actual registration here in case any other event description is invalid.
|
||||
// In that case we don't need to roll back any partial registration.
|
||||
DynamicEventInfo info{nsCString(aCategory), method, object,
|
||||
nsTArray<nsCString>(extra_keys), recordOnRelease};
|
||||
newEventInfos.AppendElement(info);
|
||||
newEventExpired.AppendElement(expired);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RegisterEventResult res = ::RegisterEvents(StaticMutexAutoLock(gTelemetryEventsMutex),
|
||||
aCategory, newEventInfos, newEventExpired);
|
||||
switch (res) {
|
||||
case RegisterEventResult::AlreadyRegistered:
|
||||
JS_ReportErrorASCII(cx, "Attempt to register event that is already registered.");
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
TelemetryEvent::CreateSnapshots(uint32_t aDataset, bool aClear, JSContext* cx,
|
||||
uint8_t optional_argc, JS::MutableHandleValue aResult)
|
||||
|
@ -1140,7 +762,9 @@ TelemetryEvent::CreateSnapshots(uint32_t aDataset, bool aClear, JSContext* cx,
|
|||
const uint32_t len = eventStorage->Length();
|
||||
for (uint32_t i = 0; i < len; ++i) {
|
||||
const EventRecord& record = (*eventStorage)[i];
|
||||
if (IsInDataset(GetDataset(locker, record.GetEventKey()), aDataset)) {
|
||||
const EventInfo& info = gEventInfo[record.EventId()];
|
||||
|
||||
if (IsInDataset(info.common_info.dataset, aDataset)) {
|
||||
events.AppendElement(record);
|
||||
}
|
||||
}
|
||||
|
@ -1166,7 +790,7 @@ TelemetryEvent::CreateSnapshots(uint32_t aDataset, bool aClear, JSContext* cx,
|
|||
for (uint32_t i = 0; i < processLength; ++i)
|
||||
{
|
||||
JS::RootedObject eventsArray(cx);
|
||||
if (NS_FAILED(SerializeEventsArray(processEvents[i].second(), cx, &eventsArray, aDataset))) {
|
||||
if (NS_FAILED(SerializeEventsArray(processEvents[i].second(), cx, &eventsArray))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -1207,9 +831,9 @@ TelemetryEvent::SetEventRecordingEnabled(const nsACString& category, bool enable
|
|||
}
|
||||
|
||||
if (enabled) {
|
||||
gEnabledCategories.PutEntry(category);
|
||||
gEnabledCategories.PutEntry(categoryId);
|
||||
} else {
|
||||
gEnabledCategories.RemoveEntry(category);
|
||||
gEnabledCategories.RemoveEntry(categoryId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1243,10 +867,5 @@ TelemetryEvent::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
|
|||
|
||||
n += gEnabledCategories.ShallowSizeOfExcludingThis(aMallocSizeOf);
|
||||
|
||||
n += gDynamicEventInfo.ShallowSizeOfExcludingThis(aMallocSizeOf);
|
||||
for (auto& info : gDynamicEventInfo) {
|
||||
n += info.SizeOfExcludingThis(aMallocSizeOf);
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
|
|
@ -33,11 +33,7 @@ nsresult RecordEvent(const nsACString& aCategory, const nsACString& aMethod,
|
|||
const nsACString& aObject, JS::HandleValue aValue,
|
||||
JS::HandleValue aExtra, JSContext* aCx,
|
||||
uint8_t optional_argc);
|
||||
|
||||
void SetEventRecordingEnabled(const nsACString& aCategory, bool aEnabled);
|
||||
nsresult RegisterEvents(const nsACString& aCategory, JS::Handle<JS::Value> aEventData,
|
||||
JSContext* cx);
|
||||
|
||||
nsresult CreateSnapshots(uint32_t aDataset, bool aClear, JSContext* aCx,
|
||||
uint8_t optional_argc, JS::MutableHandleValue aResult);
|
||||
|
||||
|
|
|
@ -1009,8 +1009,8 @@ var Impl = {
|
|||
return [];
|
||||
}
|
||||
|
||||
let snapshot = Telemetry.snapshotEvents(this.getDatasetType(),
|
||||
clearSubsession);
|
||||
let snapshot = Telemetry.snapshotBuiltinEvents(this.getDatasetType(),
|
||||
clearSubsession);
|
||||
|
||||
// Don't return the test events outside of test environments.
|
||||
if (!this._testing) {
|
||||
|
@ -1323,9 +1323,6 @@ var Impl = {
|
|||
keyedHistograms: keyedHistograms["extension"],
|
||||
events: events["extension"] || [],
|
||||
},
|
||||
dynamic: {
|
||||
events: events["dynamic"] || [],
|
||||
},
|
||||
};
|
||||
|
||||
// Only include the GPU process if we've accumulated data for it.
|
||||
|
|
|
@ -66,7 +66,7 @@ The YAML definition file
|
|||
========================
|
||||
|
||||
Any event recorded into Firefox Telemetry must be registered before it can be recorded.
|
||||
For any code that ships as part of Firefox that happens in `Events.yaml <https://dxr.mozilla.org/mozilla-central/source/toolkit/components/telemetry/Events.yaml>`_.
|
||||
This happens in `Events.yaml <https://dxr.mozilla.org/mozilla-central/source/toolkit/components/telemetry/Events.yaml>`_.
|
||||
|
||||
The probes in the definition file are represented in a fixed-depth, three-level structure. The first level contains *category* names (grouping multiple events together), the second level contains *event* names, under which the events properties are listed. E.g.:
|
||||
|
||||
|
@ -155,9 +155,6 @@ Example:
|
|||
// event: [982134, "ui", "completion", "search-bar", "yahoo",
|
||||
// {"qerylen": "7", "results": "23"}]
|
||||
|
||||
``setEventRecordingEnabled()``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: js
|
||||
|
||||
Services.telemetry.setEventRecordingEnabled(category, enabled);
|
||||
|
@ -173,49 +170,12 @@ Example:
|
|||
Services.telemetry.setEventRecordingEnabled("ui", false);
|
||||
// ... now "ui" events will not be recorded anymore.
|
||||
|
||||
``registerEvents()``
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: js
|
||||
|
||||
Services.telemetry.registerEvents(category, eventData);
|
||||
|
||||
Register new events from add-ons.
|
||||
|
||||
* ``category`` - *(required, string)* The category the events are in.
|
||||
* ``eventData`` - *(required, object)* An object of the form ``{eventName1: event1Data, ...}``, where each events data is an object with the entries:
|
||||
|
||||
* ``methods`` - *(required, list of strings)* The valid event methods.
|
||||
* ``objects`` - *(required, list of strings)* The valid event objects.
|
||||
* ``extra_keys`` - *(optional, list of strings)* The valid extra keys for the event.
|
||||
* ``record_on_release`` - *(optional, bool)*
|
||||
|
||||
For events recorded from add-ons, registration happens at runtime. Any new events must first be registered through this function before they can be recorded.
|
||||
The registered categories will automatically be enabled for recording.
|
||||
|
||||
After registration, the events can be recorded through the ``recordEvent()`` function. They will be submitted in the main pings payload under ``processes.dynamic.events``.
|
||||
|
||||
New events registered here are subject to the same limitations as the ones registered through ``Events.yaml``, although the naming was in parts updated to recent policy changes.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: js
|
||||
|
||||
Services.telemetry.registerEvents("myAddon.interaction", {
|
||||
"click": {
|
||||
methods: ["click"],
|
||||
objects: ["red_button", "blue_button"],
|
||||
}
|
||||
});
|
||||
// Now events can be recorded.
|
||||
Services.telemetry.recordEvent("myAddon.interaction", "click", "red_button");
|
||||
|
||||
Internal API
|
||||
------------
|
||||
~~~~~~~~~~~~
|
||||
|
||||
.. code-block:: js
|
||||
|
||||
Services.telemetry.snapshotEvents(dataset, clear);
|
||||
Services.telemetry.snapshotBuiltinEvents(dataset, clear);
|
||||
Services.telemetry.clearEvents();
|
||||
|
||||
These functions are only supposed to be used by Telemetry internally or in tests.
|
||||
|
@ -226,4 +186,3 @@ Version History
|
|||
- Firefox 52: Initial event support (`bug 1302663 <https://bugzilla.mozilla.org/show_bug.cgi?id=1302663>`_).
|
||||
- Firefox 53: Event recording disabled by default (`bug 1329139 <https://bugzilla.mozilla.org/show_bug.cgi?id=1329139>`_).
|
||||
- Firefox 54: Added child process events (`bug 1313326 <https://bugzilla.mozilla.org/show_bug.cgi?id=1313326>`_).
|
||||
- Firefox 56: Added support for recording new probes from add-ons (`bug 1302681 <bug https://bugzilla.mozilla.org/show_bug.cgi?id=1302681>`_).
|
||||
|
|
|
@ -498,35 +498,7 @@ interface nsITelemetry : nsISupports
|
|||
* @param [aClear=false] Whether to clear out the scalars after snapshotting.
|
||||
*/
|
||||
[implicit_jscontext, optional_argc]
|
||||
jsval snapshotEvents(in uint32_t aDataset, [optional] in boolean aClear);
|
||||
|
||||
/**
|
||||
* Register new events to record them from addons. This allows registering multiple
|
||||
* events for a category. They will be valid only for the current Firefox session.
|
||||
* Note that events shipping in Firefox should be registered in Events.yaml.
|
||||
*
|
||||
* @param aCategory The unique category the events are registered in.
|
||||
* @param aEventData An object that contains registration data for 1-N events of the form:
|
||||
* {
|
||||
* "categoryName": {
|
||||
* "methods": ["test1"],
|
||||
* "objects": ["object1"],
|
||||
* "record_on_release": false,
|
||||
* "extra_keys": ["key1", "key2"], // optional
|
||||
* "expired": false // optional, defaults to false.
|
||||
* },
|
||||
* ...
|
||||
* }
|
||||
* @param aEventData.<name>.methods List of methods for this event entry.
|
||||
* @param aEventData.<name>.objects List of objects for this event entry.
|
||||
* @param aEventData.<name>.extra_keys Optional, list of allowed extra keys for this event entry.
|
||||
* @param aEventData.<name>.record_on_release Optional, whether to record this data on release.
|
||||
* Defaults to false.
|
||||
* @param aEventData.<name>.expired Optional, whether this event entry is expired. This allows
|
||||
* recording it without error, but it will be discarded. Defaults to false.
|
||||
*/
|
||||
[implicit_jscontext]
|
||||
void registerEvents(in ACString aCategory, in jsval aEventData);
|
||||
jsval snapshotBuiltinEvents(in uint32_t aDataset, [optional] in boolean aClear);
|
||||
|
||||
/**
|
||||
* Resets all the stored events. This is intended to be only used in tests.
|
||||
|
|
|
@ -36,18 +36,11 @@ const UNRECORDED_PARENT_EVENTS = [
|
|||
["telemetry.test", "content_only", "object1"],
|
||||
];
|
||||
|
||||
const RECORDED_DYNAMIC_EVENTS = [
|
||||
["telemetry.test.dynamic", "test1", "object1"],
|
||||
["telemetry.test.dynamic", "test2", "object1"],
|
||||
];
|
||||
|
||||
function run_child_test() {
|
||||
// Record some events in the "content" process.
|
||||
RECORDED_CONTENT_EVENTS.forEach(e => Telemetry.recordEvent(...e));
|
||||
// These events should not be recorded for the content process.
|
||||
UNRECORDED_CONTENT_EVENTS.forEach(e => Telemetry.recordEvent(...e));
|
||||
// Record some dynamic events from the content process.
|
||||
RECORDED_DYNAMIC_EVENTS.forEach(e => Telemetry.recordEvent(...e));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -57,9 +50,8 @@ function run_child_test() {
|
|||
async function waitForContentEvents() {
|
||||
await ContentTaskUtils.waitForCondition(() => {
|
||||
const snapshot =
|
||||
Telemetry.snapshotEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
|
||||
return Object.keys(snapshot).includes("content") &&
|
||||
Object.keys(snapshot).includes("dynamic");
|
||||
Telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
|
||||
return Object.keys(snapshot).includes("content");
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -82,21 +74,6 @@ add_task(async function() {
|
|||
// Enable recording for the test event category.
|
||||
Telemetry.setEventRecordingEnabled("telemetry.test", true);
|
||||
|
||||
// Register dynamic test events.
|
||||
Telemetry.registerEvents("telemetry.test.dynamic", {
|
||||
// Event with only required fields.
|
||||
"test1": {
|
||||
methods: ["test1"],
|
||||
objects: ["object1"],
|
||||
},
|
||||
// Event with extra_keys.
|
||||
"test2": {
|
||||
methods: ["test2", "test2b"],
|
||||
objects: ["object1"],
|
||||
extra_keys: ["key1", "key2"],
|
||||
},
|
||||
});
|
||||
|
||||
// Run test in child, don't wait for it to finish: just wait for the
|
||||
// MESSAGE_CHILD_TEST_DONE.
|
||||
const timestampBeforeChildEvents = Telemetry.msSinceProcessStart();
|
||||
|
@ -123,8 +100,6 @@ add_task(async function() {
|
|||
Assert.ok("events" in payload.processes.parent, "Main process section should have events.");
|
||||
Assert.ok("content" in payload.processes, "Should have child process section");
|
||||
Assert.ok("events" in payload.processes.content, "Child process section should have events.");
|
||||
Assert.ok("dynamic" in payload.processes, "Should have dynamic process section");
|
||||
Assert.ok("events" in payload.processes.dynamic, "Dynamic process section should have events.");
|
||||
|
||||
// Check that the expected events are present from the content process.
|
||||
let contentEvents = payload.processes.content.events.map(e => e.slice(1));
|
||||
|
@ -140,13 +115,6 @@ add_task(async function() {
|
|||
Assert.deepEqual(parentEvents[i], RECORDED_PARENT_EVENTS[i], "Should have recorded expected event.");
|
||||
}
|
||||
|
||||
// Check that the expected dynamic events are present.
|
||||
let dynamicEvents = payload.processes.dynamic.events.map(e => e.slice(1));
|
||||
Assert.equal(dynamicEvents.length, RECORDED_DYNAMIC_EVENTS.length, "Should match expected event count.");
|
||||
for (let i = 0; i < RECORDED_DYNAMIC_EVENTS.length; ++i) {
|
||||
Assert.deepEqual(dynamicEvents[i], RECORDED_DYNAMIC_EVENTS[i], "Should have recorded expected event.");
|
||||
}
|
||||
|
||||
// Check that the event timestamps are in the expected ranges.
|
||||
let contentTimestamps = payload.processes.content.events.map(e => e[0]);
|
||||
let parentTimestamps = payload.processes.parent.events.map(e => e[0]);
|
||||
|
@ -159,9 +127,9 @@ add_task(async function() {
|
|||
|
||||
// Make sure all events are cleared from storage properly.
|
||||
let snapshot =
|
||||
Telemetry.snapshotEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true);
|
||||
Telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true);
|
||||
Assert.greaterOrEqual(Object.keys(snapshot).length, 2, "Should have events from at least two processes.");
|
||||
snapshot =
|
||||
Telemetry.snapshotEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true);
|
||||
Telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, true);
|
||||
Assert.equal(Object.keys(snapshot).length, 0, "Should have cleared all events from storage.");
|
||||
});
|
||||
|
|
|
@ -44,13 +44,13 @@ add_task(async function test_recording_state() {
|
|||
|
||||
// Both test categories should be off by default.
|
||||
events.forEach(e => Telemetry.recordEvent(...e));
|
||||
let snapshot = Telemetry.snapshotEvents(OPTIN, true);
|
||||
let snapshot = Telemetry.snapshotBuiltinEvents(OPTIN, true);
|
||||
Assert.equal(Object.keys(snapshot).length, 0, "Should not have recorded any events.");
|
||||
|
||||
// Enable one test category and see that we record correctly.
|
||||
Telemetry.setEventRecordingEnabled("telemetry.test", true);
|
||||
events.forEach(e => Telemetry.recordEvent(...e));
|
||||
snapshot = Telemetry.snapshotEvents(OPTIN, true);
|
||||
snapshot = Telemetry.snapshotBuiltinEvents(OPTIN, true);
|
||||
Assert.ok(("parent" in snapshot), "Should have entry for main process.");
|
||||
Assert.equal(snapshot.parent.length, 1, "Should have recorded one event.");
|
||||
Assert.equal(snapshot.parent[0][1], "telemetry.test", "Should have recorded one event in telemetry.test");
|
||||
|
@ -58,7 +58,7 @@ add_task(async function test_recording_state() {
|
|||
// Also enable the other test category and see that we record correctly.
|
||||
Telemetry.setEventRecordingEnabled("telemetry.test.second", true);
|
||||
events.forEach(e => Telemetry.recordEvent(...e));
|
||||
snapshot = Telemetry.snapshotEvents(OPTIN, true);
|
||||
snapshot = Telemetry.snapshotBuiltinEvents(OPTIN, true);
|
||||
Assert.ok(("parent" in snapshot), "Should have entry for main process.");
|
||||
Assert.equal(snapshot.parent.length, 2, "Should have recorded two events.");
|
||||
Assert.equal(snapshot.parent[0][1], "telemetry.test", "Should have recorded one event in telemetry.test");
|
||||
|
@ -67,7 +67,7 @@ add_task(async function test_recording_state() {
|
|||
// Now turn of one category again and check that this works as expected.
|
||||
Telemetry.setEventRecordingEnabled("telemetry.test", false);
|
||||
events.forEach(e => Telemetry.recordEvent(...e));
|
||||
snapshot = Telemetry.snapshotEvents(OPTIN, true);
|
||||
snapshot = Telemetry.snapshotBuiltinEvents(OPTIN, true);
|
||||
Assert.ok(("parent" in snapshot), "Should have entry for main process.");
|
||||
Assert.equal(snapshot.parent.length, 1, "Should have recorded one event.");
|
||||
Assert.equal(snapshot.parent[0][1], "telemetry.test.second", "Should have recorded one event in telemetry.test.second");
|
||||
|
@ -145,12 +145,12 @@ add_task(async function test_recording() {
|
|||
};
|
||||
|
||||
// Check that the expected events were recorded.
|
||||
let snapshot = Telemetry.snapshotEvents(OPTIN, false);
|
||||
let snapshot = Telemetry.snapshotBuiltinEvents(OPTIN, false);
|
||||
Assert.ok(("parent" in snapshot), "Should have entry for main process.");
|
||||
checkEvents(snapshot.parent, expected);
|
||||
|
||||
// Check serializing only opt-out events.
|
||||
snapshot = Telemetry.snapshotEvents(OPTOUT, false);
|
||||
snapshot = Telemetry.snapshotBuiltinEvents(OPTOUT, false);
|
||||
Assert.ok(("parent" in snapshot), "Should have entry for main process.");
|
||||
let filtered = expected.filter(e => e.optout == true);
|
||||
checkEvents(snapshot.parent, filtered);
|
||||
|
@ -167,12 +167,12 @@ add_task(async function test_clear() {
|
|||
|
||||
// Check that events were recorded.
|
||||
// The events are cleared by passing the respective flag.
|
||||
let snapshot = Telemetry.snapshotEvents(OPTIN, true);
|
||||
let snapshot = Telemetry.snapshotBuiltinEvents(OPTIN, true);
|
||||
Assert.ok(("parent" in snapshot), "Should have entry for main process.");
|
||||
Assert.equal(snapshot.parent.length, 2 * COUNT, `Should have recorded ${2 * COUNT} events.`);
|
||||
|
||||
// Now the events should be cleared.
|
||||
snapshot = Telemetry.snapshotEvents(OPTIN, false);
|
||||
snapshot = Telemetry.snapshotBuiltinEvents(OPTIN, false);
|
||||
Assert.equal(Object.keys(snapshot).length, 0, `Should have cleared the events.`);
|
||||
});
|
||||
|
||||
|
@ -181,17 +181,17 @@ add_task(async function test_expiry() {
|
|||
|
||||
// Recording call with event that is expired by version.
|
||||
Telemetry.recordEvent("telemetry.test", "expired_version", "object1");
|
||||
let snapshot = Telemetry.snapshotEvents(OPTIN, true);
|
||||
let snapshot = Telemetry.snapshotBuiltinEvents(OPTIN, true);
|
||||
Assert.equal(Object.keys(snapshot).length, 0, "Should not record event with expired version.");
|
||||
|
||||
// Recording call with event that is expired by date.
|
||||
Telemetry.recordEvent("telemetry.test", "expired_date", "object1");
|
||||
snapshot = Telemetry.snapshotEvents(OPTIN, true);
|
||||
snapshot = Telemetry.snapshotBuiltinEvents(OPTIN, true);
|
||||
Assert.equal(Object.keys(snapshot).length, 0, "Should not record event with expired date.");
|
||||
|
||||
// Recording call with event that has expiry_version and expiry_date in the future.
|
||||
Telemetry.recordEvent("telemetry.test", "not_expired_optout", "object1");
|
||||
snapshot = Telemetry.snapshotEvents(OPTOUT, true);
|
||||
snapshot = Telemetry.snapshotBuiltinEvents(OPTOUT, true);
|
||||
Assert.ok(("parent" in snapshot), "Should have entry for main process.");
|
||||
Assert.equal(snapshot.parent.length, 1, "Should record event when date and version are not expired.");
|
||||
});
|
||||
|
@ -201,22 +201,22 @@ add_task(async function test_invalidParams() {
|
|||
|
||||
// Recording call with wrong type for value argument.
|
||||
Telemetry.recordEvent("telemetry.test", "test1", "object1", 1);
|
||||
let snapshot = Telemetry.snapshotEvents(OPTIN, true);
|
||||
let snapshot = Telemetry.snapshotBuiltinEvents(OPTIN, true);
|
||||
Assert.equal(Object.keys(snapshot).length, 0, "Should not record event when value argument with invalid type is passed.");
|
||||
|
||||
// Recording call with wrong type for extra argument.
|
||||
Telemetry.recordEvent("telemetry.test", "test1", "object1", null, "invalid");
|
||||
snapshot = Telemetry.snapshotEvents(OPTIN, true);
|
||||
snapshot = Telemetry.snapshotBuiltinEvents(OPTIN, true);
|
||||
Assert.equal(Object.keys(snapshot).length, 0, "Should not record event when extra argument with invalid type is passed.");
|
||||
|
||||
// Recording call with unknown extra key.
|
||||
Telemetry.recordEvent("telemetry.test", "test1", "object1", null, {"key3": "x"});
|
||||
snapshot = Telemetry.snapshotEvents(OPTIN, true);
|
||||
snapshot = Telemetry.snapshotBuiltinEvents(OPTIN, true);
|
||||
Assert.equal(Object.keys(snapshot).length, 0, "Should not record event when extra argument with invalid key is passed.");
|
||||
|
||||
// Recording call with invalid value type.
|
||||
Telemetry.recordEvent("telemetry.test", "test1", "object1", null, {"key3": 1});
|
||||
snapshot = Telemetry.snapshotEvents(OPTIN, true);
|
||||
snapshot = Telemetry.snapshotBuiltinEvents(OPTIN, true);
|
||||
Assert.equal(Object.keys(snapshot).length, 0, "Should not record event when extra argument with invalid value type is passed.");
|
||||
});
|
||||
|
||||
|
@ -231,7 +231,7 @@ add_task(async function test_storageLimit() {
|
|||
}
|
||||
|
||||
// Check that the right events were recorded.
|
||||
let snapshot = Telemetry.snapshotEvents(OPTIN, true);
|
||||
let snapshot = Telemetry.snapshotBuiltinEvents(OPTIN, true);
|
||||
Assert.ok(("parent" in snapshot), "Should have entry for main process.");
|
||||
let events = snapshot.parent;
|
||||
Assert.equal(events.length, LIMIT, `Should have only recorded ${LIMIT} events`);
|
||||
|
@ -274,7 +274,7 @@ add_task(async function test_valueLimits() {
|
|||
}
|
||||
|
||||
// Check that the right events were recorded.
|
||||
let snapshot = Telemetry.snapshotEvents(OPTIN, true);
|
||||
let snapshot = Telemetry.snapshotBuiltinEvents(OPTIN, true);
|
||||
Assert.ok(("parent" in snapshot), "Should have entry for main process.");
|
||||
let events = snapshot.parent;
|
||||
Assert.equal(events.length, expected.length,
|
||||
|
@ -294,239 +294,10 @@ add_task(async function test_unicodeValues() {
|
|||
Telemetry.recordEvent("telemetry.test", "test1", "object1", null, {"key1": value});
|
||||
|
||||
// Check that the values were correctly recorded.
|
||||
let snapshot = Telemetry.snapshotEvents(OPTIN, true);
|
||||
let snapshot = Telemetry.snapshotBuiltinEvents(OPTIN, true);
|
||||
Assert.ok(("parent" in snapshot), "Should have entry for main process.");
|
||||
let events = snapshot.parent;
|
||||
Assert.equal(events.length, 2, "Should have recorded 2 events.");
|
||||
Assert.equal(events[0][4], value, "Should have recorded the right value.");
|
||||
Assert.equal(events[1][5]["key1"], value, "Should have recorded the right extra value.");
|
||||
});
|
||||
|
||||
add_task(function* test_dynamicEvents() {
|
||||
Telemetry.clearEvents();
|
||||
Telemetry.canRecordExtended = true;
|
||||
|
||||
// Register some test events.
|
||||
Telemetry.registerEvents("telemetry.test.dynamic", {
|
||||
// Event with only required fields.
|
||||
"test1": {
|
||||
methods: ["test1"],
|
||||
objects: ["object1"],
|
||||
},
|
||||
// Event with extra_keys.
|
||||
"test2": {
|
||||
methods: ["test2", "test2b"],
|
||||
objects: ["object1"],
|
||||
extra_keys: ["key1", "key2"],
|
||||
},
|
||||
// Expired event.
|
||||
"test3": {
|
||||
methods: ["test3"],
|
||||
objects: ["object1"],
|
||||
expired: true,
|
||||
},
|
||||
// A release-channel recording event.
|
||||
"test4": {
|
||||
methods: ["test4"],
|
||||
objects: ["object1"],
|
||||
record_on_release: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Record some valid events.
|
||||
Telemetry.recordEvent("telemetry.test.dynamic", "test1", "object1");
|
||||
Telemetry.recordEvent("telemetry.test.dynamic", "test2", "object1", null,
|
||||
{"key1": "foo", "key2": "bar"});
|
||||
Telemetry.recordEvent("telemetry.test.dynamic", "test3", "object1", "some value");
|
||||
Telemetry.recordEvent("telemetry.test.dynamic", "test4", "object1", null);
|
||||
|
||||
// Test recording an unknown event.
|
||||
Assert.throws(() => Telemetry.recordEvent("telemetry.test.dynamic", "unknown", "unknown"),
|
||||
/Error: Unknown event: \["telemetry\.test\.dynamic", "unknown", "unknown"\]/,
|
||||
"Should throw when recording an unknown dynamic event.");
|
||||
|
||||
// Now check that the snapshot contains the expected data.
|
||||
let snapshot = Telemetry.snapshotEvents(OPTIN, false);
|
||||
Assert.ok(("dynamic" in snapshot), "Should have dynamic events in the snapshot.");
|
||||
|
||||
let expected = [
|
||||
["telemetry.test.dynamic", "test1", "object1"],
|
||||
["telemetry.test.dynamic", "test2", "object1", null, {key1: "foo", key2: "bar"}],
|
||||
// "test3" is epxired, so it should not be recorded.
|
||||
["telemetry.test.dynamic", "test4", "object1"],
|
||||
];
|
||||
let events = snapshot.dynamic;
|
||||
Assert.equal(events.length, expected.length, "Should have recorded the right amount of events.");
|
||||
for (let i = 0; i < expected.length; ++i) {
|
||||
Assert.deepEqual(events[i].slice(1), expected[i],
|
||||
"Should have recorded the expected event data.");
|
||||
}
|
||||
|
||||
// Check that the opt-out snapshot contains only the one expected event.
|
||||
snapshot = Telemetry.snapshotEvents(OPTOUT, false);
|
||||
Assert.ok(("dynamic" in snapshot), "Should have dynamic events in the snapshot.");
|
||||
Assert.equal(snapshot.dynamic.length, 1, "Should have one opt-out event in the snapshot.");
|
||||
expected = ["telemetry.test.dynamic", "test4", "object1"];
|
||||
Assert.deepEqual(snapshot.dynamic[0].slice(1), expected);
|
||||
|
||||
// Recording with unknown extra keys should be ignored and print an error.
|
||||
Telemetry.clearEvents();
|
||||
Telemetry.recordEvent("telemetry.test.dynamic", "test1", "object1", null, {"key1": "foo"});
|
||||
Telemetry.recordEvent("telemetry.test.dynamic", "test2", "object1", null, {"key1": "foo", "unknown": "bar"});
|
||||
snapshot = Telemetry.snapshotEvents(OPTIN, true);
|
||||
Assert.ok(!("dynamic" in snapshot), "Should have not recorded dynamic events with unknown extra keys.");
|
||||
|
||||
// Other built-in events should not show up in the "dynamic" bucket of the snapshot.
|
||||
Telemetry.recordEvent("telemetry.test", "test1", "object1");
|
||||
snapshot = Telemetry.snapshotEvents(OPTIN, true);
|
||||
Assert.ok(!("dynamic" in snapshot), "Should have not recorded built-in event into dynamic bucket.");
|
||||
|
||||
// Test that recording opt-in and opt-out events works as expected.
|
||||
Telemetry.clearEvents();
|
||||
Telemetry.canRecordExtended = false;
|
||||
|
||||
Telemetry.recordEvent("telemetry.test.dynamic", "test1", "object1");
|
||||
Telemetry.recordEvent("telemetry.test.dynamic", "test4", "object1");
|
||||
|
||||
expected = [
|
||||
// Only "test4" should have been recorded.
|
||||
["telemetry.test.dynamic", "test4", "object1"],
|
||||
];
|
||||
snapshot = Telemetry.snapshotEvents(OPTIN, true);
|
||||
Assert.equal(snapshot.dynamic.length, 1, "Should have one opt-out event in the snapshot.");
|
||||
Assert.deepEqual(snapshot.dynamic.map(e => e.slice(1)), expected);
|
||||
});
|
||||
|
||||
add_task(function* test_dynamicEventRegistrationValidation() {
|
||||
Telemetry.canRecordExtended = true;
|
||||
Telemetry.clearEvents();
|
||||
|
||||
// Test registration of invalid categories.
|
||||
Assert.throws(() => Telemetry.registerEvents("telemetry+test+dynamic", {
|
||||
"test1": {
|
||||
methods: ["test1"],
|
||||
objects: ["object1"],
|
||||
},
|
||||
}),
|
||||
/Category parameter should match the identifier pattern\./,
|
||||
"Should throw when registering category names with invalid characters.");
|
||||
Assert.throws(() => Telemetry.registerEvents("telemetry.test.test.test.test.test.test.test.test", {
|
||||
"test1": {
|
||||
methods: ["test1"],
|
||||
objects: ["object1"],
|
||||
},
|
||||
}),
|
||||
/Category parameter should match the identifier pattern\./,
|
||||
"Should throw when registering overly long category names.");
|
||||
|
||||
// Test registration of invalid event names.
|
||||
Assert.throws(() => Telemetry.registerEvents("telemetry.test.dynamic1", {
|
||||
"test?1": {
|
||||
methods: ["test1"],
|
||||
objects: ["object1"],
|
||||
},
|
||||
}),
|
||||
/Event names should match the identifier pattern\./,
|
||||
"Should throw when registering event names with invalid characters.");
|
||||
Assert.throws(() => Telemetry.registerEvents("telemetry.test.dynamic2", {
|
||||
"test1test1test1test1test1test1test1": {
|
||||
methods: ["test1"],
|
||||
objects: ["object1"],
|
||||
},
|
||||
}),
|
||||
/Event names should match the identifier pattern\./,
|
||||
"Should throw when registering overly long event names.");
|
||||
|
||||
// Test registration of invalid method names.
|
||||
Assert.throws(() => Telemetry.registerEvents("telemetry.test.dynamic3", {
|
||||
"test1": {
|
||||
methods: ["test?1"],
|
||||
objects: ["object1"],
|
||||
},
|
||||
}),
|
||||
/Method names should match the identifier pattern\./,
|
||||
"Should throw when registering method names with invalid characters.");
|
||||
Assert.throws(() => Telemetry.registerEvents("telemetry.test.dynamic", {
|
||||
"test1": {
|
||||
methods: ["test1test1test1test1test1test1test1"],
|
||||
objects: ["object1"],
|
||||
},
|
||||
}),
|
||||
/Method names should match the identifier pattern\./,
|
||||
"Should throw when registering overly long method names.");
|
||||
|
||||
// Test registration of invalid object names.
|
||||
Assert.throws(() => Telemetry.registerEvents("telemetry.test.dynamic4", {
|
||||
"test1": {
|
||||
methods: ["test1"],
|
||||
objects: ["object?1"],
|
||||
},
|
||||
}),
|
||||
/Object names should match the identifier pattern\./,
|
||||
"Should throw when registering object names with invalid characters.");
|
||||
Assert.throws(() => Telemetry.registerEvents("telemetry.test.dynamic5", {
|
||||
"test1": {
|
||||
methods: ["test1"],
|
||||
objects: ["object1object1object1object1object1object1"],
|
||||
},
|
||||
}),
|
||||
/Object names should match the identifier pattern\./,
|
||||
"Should throw when registering overly long object names.");
|
||||
|
||||
// Test validation of invalid key names.
|
||||
Assert.throws(() => Telemetry.registerEvents("telemetry.test.dynamic6", {
|
||||
"test1": {
|
||||
methods: ["test1"],
|
||||
objects: ["object1"],
|
||||
extra_keys: ["a?1"],
|
||||
},
|
||||
}),
|
||||
/Extra key names should match the identifier pattern\./,
|
||||
"Should throw when registering extra key names with invalid characters.");
|
||||
|
||||
// Test validation of key names that are too long - we allow a maximum of 15 characters.
|
||||
Assert.throws(() => Telemetry.registerEvents("telemetry.test.dynamic7", {
|
||||
"test1": {
|
||||
methods: ["test1"],
|
||||
objects: ["object1"],
|
||||
extra_keys: ["a012345678901234"],
|
||||
},
|
||||
}),
|
||||
/Extra key names should match the identifier pattern\./,
|
||||
"Should throw when registering extra key names which are too long.");
|
||||
Telemetry.registerEvents("telemetry.test.dynamic8", {
|
||||
"test1": {
|
||||
methods: ["test1"],
|
||||
objects: ["object1"],
|
||||
extra_keys: ["a01234567890123"],
|
||||
},
|
||||
});
|
||||
|
||||
// Test validation of extra key count - we only allow 10.
|
||||
Assert.throws(() => Telemetry.registerEvents("telemetry.test.dynamic9", {
|
||||
"test1": {
|
||||
methods: ["test1"],
|
||||
objects: ["object1"],
|
||||
extra_keys: ["a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "a10", "a11"],
|
||||
},
|
||||
}),
|
||||
/No more than 10 extra keys can be registered\./,
|
||||
"Should throw when registering too many extra keys.");
|
||||
Telemetry.registerEvents("telemetry.test.dynamic10", {
|
||||
"test1": {
|
||||
methods: ["test1"],
|
||||
objects: ["object1"],
|
||||
extra_keys: ["a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "a10"],
|
||||
},
|
||||
});
|
||||
|
||||
// Test registering an event thats already registered through Events.yaml.
|
||||
Assert.throws(() => Telemetry.registerEvents("telemetry.test", {
|
||||
"test1": {
|
||||
methods: ["test1"],
|
||||
objects: ["object1"],
|
||||
},
|
||||
}),
|
||||
/Attempt to register event that is already registered\./,
|
||||
"Should throw when registering event that already was registered.");
|
||||
});
|
||||
|
|
|
@ -1780,7 +1780,7 @@ var Events = {
|
|||
let eventsSection = document.getElementById("events");
|
||||
removeAllChildNodes(eventsSection);
|
||||
|
||||
if (!aPayload.processes || !Object.values(aPayload.processes).find(p => "events" in p)) {
|
||||
if (!aPayload.processes || !aPayload.processes.parent) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1793,9 +1793,8 @@ var Events = {
|
|||
return;
|
||||
}
|
||||
|
||||
const hasData = Object.values(aPayload.processes).find(p => {
|
||||
return ("events" in p) && (Object.keys(p.events).length > 0);
|
||||
});
|
||||
let events = aPayload.processes[selectedProcess].events;
|
||||
const hasData = events && Object.keys(events).length > 0;
|
||||
setHasData("events-section", hasData);
|
||||
if (!hasData) {
|
||||
return;
|
||||
|
@ -1810,7 +1809,6 @@ var Events = {
|
|||
"extraHeader",
|
||||
].map(h => bundle.GetStringFromName(h));
|
||||
|
||||
let events = aPayload.processes[selectedProcess].events;
|
||||
const table = GenericTable.render(events, headings);
|
||||
eventsSection.appendChild(table);
|
||||
},
|
||||
|
@ -2094,8 +2092,7 @@ var HistogramSection = {
|
|||
}
|
||||
if (hgramsProcess &&
|
||||
"processes" in aPayload &&
|
||||
hgramsProcess in aPayload.processes &&
|
||||
"histograms" in aPayload.processes[hgramsProcess]) {
|
||||
hgramsProcess in aPayload.processes) {
|
||||
histograms = aPayload.processes[hgramsProcess].histograms;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче