Bug 814765 - Include late writes in the Telemetry ping. r=vladan.

--HG--
extra : rebase_source : d3863b0ef7f4f77411042c374227d9f41adda929
This commit is contained in:
Rafael Ávila de Espíndola 2013-01-08 09:04:37 -05:00
parent 92a0820a3a
commit 6adb826fe4
7 changed files with 215 additions and 3 deletions

View File

@ -50,6 +50,8 @@ CPPSRCS = \
Telemetry.cpp \
$(NULL)
LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/build
EXTRA_DSO_LDOPTS += \
$(MOZ_COMPONENT_LIBS) \
$(MOZ_JS_LIBS) \

View File

@ -5,6 +5,12 @@
#include <algorithm>
#ifdef XP_MACOS
#include <fstream>
#endif
#include <prio.h>
#include "mozilla/Attributes.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Likely.h"
@ -15,6 +21,7 @@
#include "nsIServiceManager.h"
#include "nsCOMArray.h"
#include "nsCOMPtr.h"
#include "nsXPCOMPrivate.h"
#include "mozilla/ModuleUtils.h"
#include "nsIXPConnect.h"
#include "mozilla/Services.h"
@ -304,6 +311,7 @@ private:
static bool AddonReflector(AddonEntryType *entry, JSContext *cx, JSObject *obj);
static bool CreateHistogramForAddon(const nsACString &name,
AddonHistogramInfo &info);
void ReadLateWritesStacks();
AddonMapType mAddonMap;
// This is used for speedy string->Telemetry::ID conversions
@ -322,6 +330,7 @@ private:
Mutex mHangReportsMutex;
nsIMemoryReporter *mMemoryReporter;
CombinedStacks mLateWritesStacks; // This is collected out of the main thread.
bool mCachedTelemetryData;
uint32_t mLastShutdownTime;
nsCOMArray<nsIFetchTelemetryDataCallback> mCallbacks;
@ -738,6 +747,7 @@ public:
NS_IMETHOD Run() {
mTelemetry->mLastShutdownTime = ReadLastShutdownDuration(mFilename);
mTelemetry->ReadLateWritesStacks();
nsCOMPtr<nsIRunnable> e =
NS_NewRunnableMethod(this, &nsFetchTelemetryData::MainThread);
NS_ENSURE_STATE(e);
@ -1533,6 +1543,150 @@ CreateJSStackObject(JSContext *cx, const CombinedStacks &stacks) {
return ret;
}
// Read a stack from the given file name. In case of any error, aStack is
// unchanged.
static void
ReadStack(const char *aFileName, Telemetry::ProcessedStack &aStack)
{
#ifdef XP_MACOS
std::ifstream file(aFileName);
size_t numModules;
file >> numModules;
if (file.fail()) {
return;
}
char newline = file.get();
if (file.fail() || newline != '\n') {
return;
}
Telemetry::ProcessedStack stack;
for (size_t i = 0; i < numModules; ++i) {
std::string moduleName;
getline(file, moduleName);
if (file.fail()) {
return;
}
Telemetry::ProcessedStack::Module module = {
moduleName,
0, // mPdbAge
"", // mPdbSignature
"" // mPdbName
};
stack.AddModule(module);
}
size_t numFrames;
file >> numFrames;
if (file.fail()) {
return;
}
newline = file.get();
if (file.fail() || newline != '\n') {
return;
}
for (size_t i = 0; i < numFrames; ++i) {
uint16_t index;
file >> index;
uintptr_t offset;
file >> std::hex >> offset >> std::dec;
if (file.fail()) {
return;
}
Telemetry::ProcessedStack::Frame frame = {
offset,
index
};
stack.AddFrame(frame);
}
aStack = stack;
#endif
}
void
TelemetryImpl::ReadLateWritesStacks()
{
nsCOMPtr<nsIFile> profileDir;
nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
getter_AddRefs(profileDir));
if (!profileDir || NS_FAILED(rv)) {
return;
}
nsAutoCString nativePath;
rv = profileDir->GetNativePath(nativePath);
if (NS_FAILED(rv)) {
return;
}
const char *name = nativePath.get();
PRDir *dir = PR_OpenDir(name);
if (!dir) {
return;
}
PRDirEntry *ent;
const char *prefix = "Telemetry.LateWriteFinal-";
unsigned int prefixLen = strlen(prefix);
while ((ent = PR_ReadDir(dir, PR_SKIP_NONE))) {
if (strncmp(prefix, ent->name, prefixLen) != 0) {
continue;
}
nsAutoCString stackNativePath = nativePath;
stackNativePath += XPCOM_FILE_PATH_SEPARATOR;
stackNativePath += nsDependentCString(ent->name);
Telemetry::ProcessedStack stack;
ReadStack(stackNativePath.get(), stack);
if (stack.GetStackSize() != 0) {
mLateWritesStacks.AddStack(stack);
}
// Delete the file so that we don't report it again on the next run.
PR_Delete(stackNativePath.get());
}
PR_CloseDir(dir);
}
NS_IMETHODIMP
TelemetryImpl::GetLateWrites(JSContext *cx, jsval *ret)
{
// The user must call AsyncReadTelemetryData first. We return an empty list
// instead of reporting a failure so that the rest of telemetry can uniformly
// handle the read not being available yet.
// FIXME: we allocate the js object again and again in the getter. We should
// figure out a way to cache it. In order to do that we have to call
// JS_AddNamedObjectRoot. A natural place to do so is in the TelemetryImpl
// constructor, but it is not clear how to get a JSContext in there.
// Another option would be to call it in here when we first call
// CreateJSStackObject, but we would still need to figure out where to call
// JS_RemoveObjectRoot. Would it be ok to never call JS_RemoveObjectRoot
// and just set the pointer to nullptr is the telemetry destructor?
JSObject *report;
if (!mCachedTelemetryData) {
CombinedStacks empty;
report = CreateJSStackObject(cx, empty);
} else {
report = CreateJSStackObject(cx, mLateWritesStacks);
}
if (report == nullptr) {
return NS_ERROR_FAILURE;
}
*ret = OBJECT_TO_JSVAL(report);
return NS_OK;
}
NS_IMETHODIMP
TelemetryImpl::GetRegisteredHistograms(JSContext *cx, jsval *ret)
{

View File

@ -518,6 +518,7 @@ TelemetryPing.prototype = {
histograms: this.getHistograms(Telemetry.histogramSnapshots),
slowSQL: Telemetry.slowSQL,
chromeHangs: Telemetry.chromeHangs,
lateWrites: Telemetry.lateWrites,
addonHistograms: this.getAddonHistograms()
};

View File

@ -87,6 +87,18 @@ interface nsITelemetry : nsISupports
[implicit_jscontext]
readonly attribute jsval chromeHangs;
/*
* An object with two fields: memoryMap and stacks.
* * memoryMap is a list of loaded libraries.
* * stacks is a list of stacks. Each stack is a list of pairs of the form
* [moduleIndex, offset]. The moduleIndex is an index into the memoryMap and
* offset is an offset in the library at memoryMap[moduleIndex].
* This format is used to make it easier to send the stacks to the
* symbolication server.
*/
[implicit_jscontext]
readonly attribute jsval lateWrites;
/**
* An object whose properties are the names of histograms defined in
* TelemetryHistograms.h and whose corresponding values are the textual
@ -193,8 +205,9 @@ interface nsITelemetry : nsISupports
readonly attribute jsval addonHistogramSnapshots;
/**
* Read data from the previous run. After the callback is called, the data is
* available in lastShutdownDuration.
* Read data from the previous run. After the callback is called, the last
* shutdown time is available in lastShutdownDuration and any late
* writes in lateWrites.
*/
void asyncFetchTelemetryData(in nsIFetchTelemetryDataCallback aCallback);
};

View File

@ -734,7 +734,29 @@ function onLoad() {
// Get the Telemetry Ping payload
Telemetry.asyncFetchTelemetryData(displayPingData);
}
};
let LateWritesSingleton = {
renderLateWrites: function LateWritesSingleton_renderLateWrites(lateWrites) {
let writesDiv = document.getElementById("late-writes-data");
clearDivData(writesDiv);
// FIXME: Add symbolication support. Refactor with the chrome hang one.
let stacks = lateWrites.stacks;
if (stacks.length == 0) {
showEmptySectionMessage("late-writes-section");
return;
}
let memoryMap = lateWrites.memoryMap;
StackRenderer.renderMemoryMap(writesDiv, memoryMap);
for (let i = 0; i < stacks.length; ++i) {
let stack = stacks[i];
StackRenderer.renderStack(writesDiv, stack);
}
}
};
function displayPingData() {
let ping = TelemetryPing.getPayload();
@ -746,6 +768,8 @@ function displayPingData() {
showEmptySectionMessage("simple-measurements-section");
}
LateWritesSingleton.renderLateWrites(ping.lateWrites);
// Show basic system info gathered
if (Object.keys(ping.info).length) {
KeyValueTable.render("system-info-table", ping.info);

View File

@ -81,6 +81,20 @@
</div>
</section>
<section id="late-writes-section" class="data-section">
<h1 class="section-name">&aboutTelemetry.lateWritesSection;</h1>
<span class="toggle-caption">&aboutTelemetry.toggleOn;</span>
<span class="toggle-caption hidden">&aboutTelemetry.toggleOff;</span>
<span class="empty-caption hidden">&aboutTelemetry.emptySection;</span>
<div id="late-writes" class="data hidden">
<!-- FIXME: add fetch-symbols and hide-symbols -->
<br/>
<br/>
<div id="late-writes-data">
</div>
</div>
</section>
<section id="system-info-section" class="data-section">
<h1 class="section-name">&aboutTelemetry.systemInfoSection;</h1>
<span class="toggle-caption">&aboutTelemetry.toggleOn;</span>

View File

@ -28,6 +28,10 @@
Simple Measurements
">
<!ENTITY aboutTelemetry.lateWritesSection "
Late Writes
">
<!ENTITY aboutTelemetry.systemInfoSection "
System Information
">