зеркало из https://github.com/mozilla/gecko-dev.git
Back out d52a31584c8a (bug 763525) to investigate the effects on TestStartupCache bustage on a CLOSED TREE
This commit is contained in:
Родитель
9dadf7e858
Коммит
74866bde56
|
@ -1189,6 +1189,346 @@ TelemetryImpl::GetHistogramById(const nsACString &name, JSContext *cx, jsval *re
|
|||
return WrapAndReturnHistogram(h, cx, ret);
|
||||
}
|
||||
|
||||
class TelemetrySessionData : public nsITelemetrySessionData
|
||||
{
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSITELEMETRYSESSIONDATA
|
||||
|
||||
public:
|
||||
static nsresult LoadFromDisk(nsIFile *, TelemetrySessionData **ptr);
|
||||
static nsresult SaveToDisk(nsIFile *, const nsACString &uuid);
|
||||
|
||||
TelemetrySessionData(const char *uuid);
|
||||
~TelemetrySessionData();
|
||||
|
||||
private:
|
||||
typedef nsBaseHashtableET<nsUint32HashKey, Histogram::SampleSet> EntryType;
|
||||
typedef AutoHashtable<EntryType> SessionMapType;
|
||||
static bool SampleReflector(EntryType *entry, JSContext *cx, JSObject *obj);
|
||||
SessionMapType mSampleSetMap;
|
||||
nsCString mUUID;
|
||||
|
||||
bool DeserializeHistogramData(Pickle &pickle, void **iter);
|
||||
static bool SerializeHistogramData(Pickle &pickle);
|
||||
|
||||
// The file format version. Should be incremented whenever we change
|
||||
// how individual SampleSets are stored in the file.
|
||||
static const unsigned int sVersion = 1;
|
||||
};
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS1(TelemetrySessionData, nsITelemetrySessionData)
|
||||
|
||||
TelemetrySessionData::TelemetrySessionData(const char *uuid)
|
||||
: mUUID(uuid)
|
||||
{
|
||||
}
|
||||
|
||||
TelemetrySessionData::~TelemetrySessionData()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelemetrySessionData::GetUuid(nsACString &uuid)
|
||||
{
|
||||
uuid = mUUID;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
TelemetrySessionData::SampleReflector(EntryType *entry, JSContext *cx,
|
||||
JSObject *snapshots)
|
||||
{
|
||||
// Don't reflect histograms with no data associated with them.
|
||||
if (entry->mData.sum() == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// This has the undesirable effect of creating a histogram for the
|
||||
// current session with the given ID. But there's no good way to
|
||||
// compute the ranges and buckets from scratch.
|
||||
Histogram *h = nsnull;
|
||||
nsresult rv = GetHistogramByEnumId(Telemetry::ID(entry->GetKey()), &h);
|
||||
if (NS_FAILED(rv)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObject *snapshot = JS_NewObject(cx, NULL, NULL, NULL);
|
||||
if (!snapshot) {
|
||||
return false;
|
||||
}
|
||||
JS::AutoObjectRooter root(cx, snapshot);
|
||||
switch (ReflectHistogramAndSamples(cx, snapshot, h, entry->mData)) {
|
||||
case REFLECT_OK:
|
||||
return JS_DefineProperty(cx, snapshots,
|
||||
h->histogram_name().c_str(),
|
||||
OBJECT_TO_JSVAL(snapshot), NULL, NULL,
|
||||
JSPROP_ENUMERATE);
|
||||
case REFLECT_CORRUPT:
|
||||
// Just ignore this one.
|
||||
return true;
|
||||
case REFLECT_FAILURE:
|
||||
return false;
|
||||
default:
|
||||
MOZ_NOT_REACHED("unhandled reflection status");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelemetrySessionData::GetSnapshots(JSContext *cx, jsval *ret)
|
||||
{
|
||||
JSObject *snapshots = JS_NewObject(cx, NULL, NULL, NULL);
|
||||
if (!snapshots) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
JS::AutoObjectRooter root(cx, snapshots);
|
||||
|
||||
if (!mSampleSetMap.ReflectHashtable(SampleReflector, cx, snapshots)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
*ret = OBJECT_TO_JSVAL(snapshots);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
TelemetrySessionData::DeserializeHistogramData(Pickle &pickle, void **iter)
|
||||
{
|
||||
uint32_t count = 0;
|
||||
if (!pickle.ReadUInt32(iter, &count)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
int stored_length;
|
||||
const char *name;
|
||||
if (!pickle.ReadData(iter, &name, &stored_length)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Telemetry::ID id;
|
||||
nsresult rv = TelemetryImpl::GetHistogramEnumId(name, &id);
|
||||
if (NS_FAILED(rv)) {
|
||||
// We serialized a non-static histogram or we serialized a
|
||||
// histogram that is no longer defined in TelemetryHistograms.h.
|
||||
// Just drop its data on the floor. If we can't deserialize the
|
||||
// data, though, we're in trouble.
|
||||
Histogram::SampleSet ss;
|
||||
if (!ss.Deserialize(iter, pickle)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
EntryType *entry = mSampleSetMap.GetEntry(id);
|
||||
if (!entry) {
|
||||
entry = mSampleSetMap.PutEntry(id);
|
||||
if (NS_UNLIKELY(!entry)) {
|
||||
return false;
|
||||
}
|
||||
if (!entry->mData.Deserialize(iter, pickle)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult
|
||||
TelemetrySessionData::LoadFromDisk(nsIFile *file, TelemetrySessionData **ptr)
|
||||
{
|
||||
*ptr = nsnull;
|
||||
nsresult rv;
|
||||
|
||||
AutoFDClose fd;
|
||||
rv = file->OpenNSPRFileDesc(PR_RDONLY, 0, &fd.rwget());
|
||||
if (NS_FAILED(rv)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// If there's not even enough data to read the header for the pickle,
|
||||
// don't bother. Conveniently, this handles the error case as well.
|
||||
int32_t size = PR_Available(fd);
|
||||
if (size < static_cast<int32_t>(sizeof(Pickle::Header))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsAutoArrayPtr<char> data(new char[size]);
|
||||
int32_t amount = PR_Read(fd, data, size);
|
||||
if (amount != size) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
Pickle pickle(data, size);
|
||||
void *iter = NULL;
|
||||
|
||||
// Make sure that how much data the pickle thinks it has corresponds
|
||||
// with how much data we actually read.
|
||||
const Pickle::Header *header = pickle.headerT<Pickle::Header>();
|
||||
if (header->payload_size != static_cast<uint32_t>(amount) - sizeof(*header)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
unsigned int storedVersion;
|
||||
if (!(pickle.ReadUInt32(&iter, &storedVersion)
|
||||
&& storedVersion == sVersion)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
const char *uuid;
|
||||
int uuidLength;
|
||||
if (!pickle.ReadData(&iter, &uuid, &uuidLength)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsAutoPtr<TelemetrySessionData> sessionData(new TelemetrySessionData(uuid));
|
||||
if (!sessionData->DeserializeHistogramData(pickle, &iter)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
*ptr = sessionData.forget();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
TelemetrySessionData::SerializeHistogramData(Pickle &pickle)
|
||||
{
|
||||
StatisticsRecorder::Histograms hs;
|
||||
StatisticsRecorder::GetHistograms(&hs);
|
||||
|
||||
if (!pickle.WriteUInt32(hs.size())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (StatisticsRecorder::Histograms::const_iterator it = hs.begin();
|
||||
it != hs.end();
|
||||
++it) {
|
||||
const Histogram *h = *it;
|
||||
const char *name = h->histogram_name().c_str();
|
||||
|
||||
// We don't check IsEmpty(h) here. We discard no-data histograms on
|
||||
// read-in, instead. It's easier to write out the number of
|
||||
// histograms required that way. (The pickle interface doesn't make
|
||||
// it easy to go back and overwrite previous data.)
|
||||
|
||||
Histogram::SampleSet ss;
|
||||
h->SnapshotSample(&ss);
|
||||
|
||||
if (!(pickle.WriteData(name, strlen(name)+1)
|
||||
&& ss.Serialize(&pickle))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult
|
||||
TelemetrySessionData::SaveToDisk(nsIFile *file, const nsACString &uuid)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
AutoFDClose fd;
|
||||
rv = file->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600, &fd.rwget());
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
Pickle pickle;
|
||||
if (!pickle.WriteUInt32(sVersion)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Include the trailing NULL for the UUID to make reading easier.
|
||||
const char *data;
|
||||
size_t length = uuid.GetData(&data);
|
||||
if (!(pickle.WriteData(data, length+1)
|
||||
&& SerializeHistogramData(pickle))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
int32_t amount = PR_Write(fd, static_cast<const char*>(pickle.data()),
|
||||
pickle.size());
|
||||
if (amount != pickle.size()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class SaveHistogramEvent : public nsRunnable
|
||||
{
|
||||
public:
|
||||
SaveHistogramEvent(nsIFile *file, const nsACString &uuid,
|
||||
nsITelemetrySaveSessionDataCallback *callback)
|
||||
: mFile(file), mUUID(uuid), mCallback(callback)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
nsresult rv = TelemetrySessionData::SaveToDisk(mFile, mUUID);
|
||||
mCallback->Handle(!!NS_SUCCEEDED(rv));
|
||||
return rv;
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIFile> mFile;
|
||||
nsCString mUUID;
|
||||
nsCOMPtr<nsITelemetrySaveSessionDataCallback> mCallback;
|
||||
};
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelemetryImpl::SaveHistograms(nsIFile *file, const nsACString &uuid,
|
||||
nsITelemetrySaveSessionDataCallback *callback,
|
||||
bool isSynchronous)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> event = new SaveHistogramEvent(file, uuid, callback);
|
||||
if (isSynchronous) {
|
||||
return event ? event->Run() : NS_ERROR_FAILURE;
|
||||
} else {
|
||||
return NS_DispatchToCurrentThread(event);
|
||||
}
|
||||
}
|
||||
|
||||
class LoadHistogramEvent : public nsRunnable
|
||||
{
|
||||
public:
|
||||
LoadHistogramEvent(nsIFile *file,
|
||||
nsITelemetryLoadSessionDataCallback *callback)
|
||||
: mFile(file), mCallback(callback)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
TelemetrySessionData *sessionData = nsnull;
|
||||
nsresult rv = TelemetrySessionData::LoadFromDisk(mFile, &sessionData);
|
||||
if (NS_FAILED(rv)) {
|
||||
mCallback->Handle(nsnull);
|
||||
} else {
|
||||
nsCOMPtr<nsITelemetrySessionData> data(sessionData);
|
||||
mCallback->Handle(data);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIFile> mFile;
|
||||
nsCOMPtr<nsITelemetryLoadSessionDataCallback> mCallback;
|
||||
};
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelemetryImpl::LoadHistograms(nsIFile *file,
|
||||
nsITelemetryLoadSessionDataCallback *callback,
|
||||
bool isSynchronous)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> event = new LoadHistogramEvent(file, callback);
|
||||
if (isSynchronous) {
|
||||
return event ? event->Run() : NS_ERROR_FAILURE;
|
||||
} else {
|
||||
return NS_DispatchToCurrentThread(event);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TelemetryImpl::GetCanRecord(bool *ret) {
|
||||
*ret = mCanRecord;
|
||||
|
|
|
@ -6,7 +6,37 @@
|
|||
#include "nsISupports.idl"
|
||||
#include "nsIFile.idl"
|
||||
|
||||
[scriptable, uuid(de54f594-4c20-4968-a27a-83b38ff952b9)]
|
||||
[scriptable, uuid(02719ffb-1a87-46cd-b8d3-5583f3267b32)]
|
||||
interface nsITelemetrySessionData : nsISupports
|
||||
{
|
||||
/**
|
||||
* The UUID of our previous session.
|
||||
*/
|
||||
readonly attribute ACString uuid;
|
||||
|
||||
/**
|
||||
* An object containing a snapshot from all registered histograms that had
|
||||
* data recorded in the previous session.
|
||||
* { name1: data1, name2: data2, .... }
|
||||
* where the individual dataN are as nsITelemetry.histogramSnapshots.
|
||||
*/
|
||||
[implicit_jscontext]
|
||||
readonly attribute jsval snapshots;
|
||||
};
|
||||
|
||||
[scriptable, function, uuid(aff36c9d-7e4c-41ab-a9b6-53773bbca0cd)]
|
||||
interface nsITelemetryLoadSessionDataCallback : nsISupports
|
||||
{
|
||||
void handle(in nsITelemetrySessionData data);
|
||||
};
|
||||
|
||||
[scriptable, function, uuid(40065f26-afd2-4417-93de-c1de9adb1548)]
|
||||
interface nsITelemetrySaveSessionDataCallback : nsISupports
|
||||
{
|
||||
void handle(in bool success);
|
||||
};
|
||||
|
||||
[scriptable, uuid(f23a2c8d-9286-42e9-ab1b-ed287eeade6d)]
|
||||
interface nsITelemetry : nsISupports
|
||||
{
|
||||
/**
|
||||
|
@ -118,6 +148,33 @@ interface nsITelemetry : nsISupports
|
|||
[implicit_jscontext]
|
||||
jsval getHistogramById(in ACString id);
|
||||
|
||||
/**
|
||||
* Save persistent histograms to the given file.
|
||||
*
|
||||
* @param file - filename for saving
|
||||
* @param uuid - UUID of this session
|
||||
* @param callback - function to be caled when file writing is complete
|
||||
*/
|
||||
void saveHistograms(in nsIFile file, in ACString uuid,
|
||||
in nsITelemetrySaveSessionDataCallback callback,
|
||||
in bool isSynchronous);
|
||||
|
||||
/* Reconstruct an nsITelemetryDataSession object containing histogram
|
||||
* information from the given file; the file must have been produced
|
||||
* via saveHistograms.
|
||||
*
|
||||
* This method does not modify the histogram information being
|
||||
* collected in the current session.
|
||||
*
|
||||
* The reconstructed object is then passed to the given callback.
|
||||
*
|
||||
* @param file - the file to load histogram information from
|
||||
* @param callback - function to process histogram information
|
||||
*/
|
||||
void loadHistograms(in nsIFile file,
|
||||
in nsITelemetryLoadSessionDataCallback callback,
|
||||
in bool isSynchronous);
|
||||
|
||||
/**
|
||||
* Set this to false to disable gathering of telemetry statistics.
|
||||
*/
|
||||
|
|
|
@ -286,6 +286,39 @@ function generateUUID() {
|
|||
return str.substring(1, str.length - 1);
|
||||
}
|
||||
|
||||
// Check that we do sane things when saving to disk.
|
||||
function test_loadSave()
|
||||
{
|
||||
let dirService = Cc["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Ci.nsIProperties);
|
||||
let tmpDir = dirService.get("TmpD", Ci.nsILocalFile);
|
||||
let tmpFile = tmpDir.clone();
|
||||
tmpFile.append("saved-histograms.dat");
|
||||
if (tmpFile.exists()) {
|
||||
tmpFile.remove(true);
|
||||
}
|
||||
|
||||
let saveFinished = false;
|
||||
let loadFinished = false;
|
||||
let uuid = generateUUID();
|
||||
let loadCallback = function(data) {
|
||||
do_check_true(data != null);
|
||||
do_check_eq(uuid, data.uuid);
|
||||
loadFinished = true;
|
||||
do_test_finished();
|
||||
};
|
||||
let saveCallback = function(success) {
|
||||
do_check_true(success);
|
||||
Telemetry.loadHistograms(tmpFile, loadCallback, false);
|
||||
saveFinished = true;
|
||||
};
|
||||
do_test_pending();
|
||||
Telemetry.saveHistograms(tmpFile, uuid, saveCallback, false);
|
||||
do_register_cleanup(function () do_check_true(saveFinished));
|
||||
do_register_cleanup(function () do_check_true(loadFinished));
|
||||
do_register_cleanup(function () tmpFile.remove(true));
|
||||
}
|
||||
|
||||
function run_test()
|
||||
{
|
||||
let kinds = [Telemetry.HISTOGRAM_EXPONENTIAL, Telemetry.HISTOGRAM_LINEAR]
|
||||
|
@ -309,4 +342,5 @@ function run_test()
|
|||
test_getSlowSQL();
|
||||
test_privateMode();
|
||||
test_addons();
|
||||
test_loadSave();
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче