Back out d52a31584c8a (bug 763525) to investigate the effects on TestStartupCache bustage on a CLOSED TREE

This commit is contained in:
Phil Ringnalda 2012-06-11 16:37:14 -07:00
parent f14d72dae8
commit cc83260853
3 changed files with 432 additions and 1 deletions

View File

@ -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;

View File

@ -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.
*/

View File

@ -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();
}