1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#include "mozilla/glean/bindings/Timespan.h"
#include "nsString.h"
#include "mozilla/Components.h"
#include "mozilla/ResultVariant.h"
#include "mozilla/dom/GleanMetricsBinding.h"
#include "mozilla/glean/bindings/ScalarGIFFTMap.h"
#include "mozilla/glean/fog_ffi_generated.h"
namespace mozilla::glean {
namespace impl {
namespace {
class ScalarIDHashKey : public PLDHashEntryHdr {
public:
using KeyType = const ScalarID&;
using KeyTypePointer = const ScalarID*;
explicit ScalarIDHashKey(KeyTypePointer aKey) : mValue(*aKey) {}
ScalarIDHashKey(ScalarIDHashKey&& aOther)
: PLDHashEntryHdr(std::move(aOther)), mValue(std::move(aOther.mValue)) {}
~ScalarIDHashKey() = default;
KeyType GetKey() const { return mValue; }
bool KeyEquals(KeyTypePointer aKey) const { return *aKey == mValue; }
static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
static PLDHashNumber HashKey(KeyTypePointer aKey) {
return static_cast<std::underlying_type<ScalarID>::type>(*aKey);
}
enum { ALLOW_MEMMOVE = true };
static_assert(std::is_trivially_copyable_v<ScalarID>);
private:
const ScalarID mValue;
};
} // namespace
using TimesToStartsMutex =
StaticDataMutex<UniquePtr<nsTHashMap<ScalarIDHashKey, TimeStamp>>>;
static Maybe<TimesToStartsMutex::AutoLock> GetTimesToStartsLock() {
static TimesToStartsMutex sTimespanStarts("sTimespanStarts");
auto lock = sTimespanStarts.Lock();
// GIFFT will work up to the end of AppShutdownTelemetry.
if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMWillShutdown)) {
return Nothing();
}
if (!*lock) {
*lock = MakeUnique<nsTHashMap<ScalarIDHashKey, TimeStamp>>();
RefPtr<nsIRunnable> cleanupFn = NS_NewRunnableFunction(__func__, [&] {
if (AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMWillShutdown)) {
auto lock = sTimespanStarts.Lock();
*lock = nullptr; // deletes, see UniquePtr.h
return;
}
RunOnShutdown(
[&] {
auto lock = sTimespanStarts.Lock();
*lock = nullptr; // deletes, see UniquePtr.h
},
ShutdownPhase::XPCOMWillShutdown);
});
// Both getting the main thread and dispatching to it can fail.
// In that event we leak. Grab a pointer so we have something to NS_RELEASE
// in that case.
nsIRunnable* temp = cleanupFn.get();
nsCOMPtr<nsIThread> mainThread;
if (NS_FAILED(NS_GetMainThread(getter_AddRefs(mainThread))) ||
NS_FAILED(mainThread->Dispatch(cleanupFn.forget(),
nsIThread::DISPATCH_NORMAL))) {
// Failed to dispatch cleanup routine.
// First, un-leak the runnable (but only if we actually attempted
// dispatch)
if (!cleanupFn) {
NS_RELEASE(temp);
}
// Next, cleanup immediately, and allow metrics to try again later.
*lock = nullptr;
return Nothing();
}
}
return Some(std::move(lock));
}
void TimespanMetric::Start() const {
auto optScalarId = ScalarIdForMetric(mId);
if (optScalarId) {
auto scalarId = optScalarId.extract();
GetTimesToStartsLock().apply([&](const auto& lock) {
(void)NS_WARN_IF(lock.ref()->Remove(scalarId));
lock.ref()->InsertOrUpdate(scalarId, TimeStamp::Now());
});
}
fog_timespan_start(mId);
}
void TimespanMetric::Stop() const {
auto optScalarId = ScalarIdForMetric(mId);
if (optScalarId) {
auto scalarId = optScalarId.extract();
GetTimesToStartsLock().apply([&](const auto& lock) {
auto optStart = lock.ref()->Extract(scalarId);
if (!NS_WARN_IF(!optStart)) {
double delta = (TimeStamp::Now() - optStart.extract()).ToMilliseconds();
uint32_t theDelta = static_cast<uint32_t>(delta);
if (delta > std::numeric_limits<uint32_t>::max()) {
theDelta = std::numeric_limits<uint32_t>::max();
} else if (MOZ_UNLIKELY(delta < 0)) {
theDelta = 0;
}
Telemetry::ScalarSet(scalarId, theDelta);
}
});
}
fog_timespan_stop(mId);
}
void TimespanMetric::Cancel() const {
auto optScalarId = ScalarIdForMetric(mId);
if (optScalarId) {
auto scalarId = optScalarId.extract();
GetTimesToStartsLock().apply(
[&](const auto& lock) { lock.ref()->Remove(scalarId); });
}
fog_timespan_cancel(mId);
}
void TimespanMetric::SetRaw(uint32_t aDuration) const {
auto optScalarId = ScalarIdForMetric(mId);
if (optScalarId) {
auto scalarId = optScalarId.extract();
Telemetry::ScalarSet(scalarId, aDuration);
}
fog_timespan_set_raw(mId, aDuration);
}
Result<Maybe<uint64_t>, nsCString> TimespanMetric::TestGetValue(
const nsACString& aPingName) const {
nsCString err;
if (fog_timespan_test_get_error(mId, &err)) {
return Err(err);
}
if (!fog_timespan_test_has_value(mId, &aPingName)) {
return Maybe<uint64_t>();
}
return Some(fog_timespan_test_get_value(mId, &aPingName));
}
} // namespace impl
/* virtual */
JSObject* GleanTimespan::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) {
return dom::GleanTimespan_Binding::Wrap(aCx, this, aGivenProto);
}
void GleanTimespan::Start() { mTimespan.Start(); }
void GleanTimespan::Stop() { mTimespan.Stop(); }
void GleanTimespan::Cancel() { mTimespan.Cancel(); }
void GleanTimespan::SetRaw(uint32_t aDuration) { mTimespan.SetRaw(aDuration); }
dom::Nullable<uint64_t> GleanTimespan::TestGetValue(const nsACString& aPingName,
ErrorResult& aRv) {
dom::Nullable<uint64_t> ret;
auto result = mTimespan.TestGetValue(aPingName);
if (result.isErr()) {
aRv.ThrowDataError(result.unwrapErr());
return ret;
}
auto optresult = result.unwrap();
if (!optresult.isNothing()) {
ret.SetValue(optresult.value());
}
return ret;
}
} // namespace mozilla::glean
|